sp-rag 0.6.0 → 0.6.2
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 +53 -22
- package/dist/cli.js +176 -44
- package/dist/lib/mcp-config.js +34 -4
- package/dist/lib/skill.js +22 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ CLI để setup nhanh SP-RAG theo hướng dev-friendly:
|
|
|
17
17
|
## Trạng thái package
|
|
18
18
|
|
|
19
19
|
- package npm public: `sp-rag`
|
|
20
|
-
- version đang publish: `0.6.
|
|
20
|
+
- version đang publish: `0.6.2`
|
|
21
21
|
- binary public: `sp-rag`
|
|
22
22
|
|
|
23
23
|
## Cài từ source trong monorepo
|
|
@@ -36,22 +36,47 @@ node dist/index.js doctor
|
|
|
36
36
|
npx sp-rag@latest install --client codex --mcp-token <grc_pat_...> --doctor
|
|
37
37
|
npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
|
|
38
38
|
npx sp-rag@latest install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
|
|
39
|
-
npx sp-rag@latest
|
|
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_...>
|
|
40
42
|
npx sp-rag@latest token verify --token <grc_pat_...>
|
|
41
|
-
npx sp-rag@latest mcp add antigravity
|
|
42
|
-
npx sp-rag@latest mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
43
|
-
npx sp-rag@latest skill install --
|
|
44
|
-
npx sp-rag@latest skill install --
|
|
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
|
|
45
47
|
```
|
|
46
48
|
|
|
47
49
|
Tương đương bằng `npm`:
|
|
48
50
|
|
|
49
51
|
```bash
|
|
50
52
|
npm exec --yes sp-rag@latest install -- --client codex --mcp-token <grc_pat_...> --doctor
|
|
51
|
-
npm exec --yes sp-rag@latest
|
|
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_...>
|
|
52
56
|
npm exec --yes sp-rag@latest token verify -- --token <grc_pat_...>
|
|
53
57
|
```
|
|
54
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
|
+
Ví dụ:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
sp-rag install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...> --doctor
|
|
73
|
+
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
74
|
+
sp-rag mcp add antigravity
|
|
75
|
+
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
76
|
+
sp-rag token add --token <grc_pat_moi>
|
|
77
|
+
sp-rag explain --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
78
|
+
```
|
|
79
|
+
|
|
55
80
|
## Lấy token ở đâu
|
|
56
81
|
|
|
57
82
|
`sp-rag` public bây giờ dùng token gắn với tài khoản thật trên server.
|
|
@@ -69,6 +94,7 @@ Luồng chuẩn:
|
|
|
69
94
|
- quyền nhìn thấy tool/resource trên MCP bám theo role của account đó
|
|
70
95
|
- `viewer`, `operator`, `approver`, `admin` sẽ thấy inventory khác nhau
|
|
71
96
|
- `MCP_SERVER_ACCESS_TOKENS_JSON` chỉ còn là fallback legacy/break-glass, không còn là luồng chính cho dev
|
|
97
|
+
- 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
|
|
72
98
|
|
|
73
99
|
Tự tạo PAT bằng API hiện có:
|
|
74
100
|
|
|
@@ -100,6 +126,7 @@ Ví dụ dev dùng token trực tiếp:
|
|
|
100
126
|
```bash
|
|
101
127
|
npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
|
|
102
128
|
npx sp-rag@latest token verify --token <grc_pat_...>
|
|
129
|
+
npx sp-rag@latest explain --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
103
130
|
```
|
|
104
131
|
|
|
105
132
|
Hoặc dùng biến môi trường:
|
|
@@ -163,7 +190,9 @@ Ghi chú:
|
|
|
163
190
|
|
|
164
191
|
```bash
|
|
165
192
|
sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
|
|
166
|
-
sp-rag
|
|
193
|
+
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
194
|
+
sp-rag explain --client codex
|
|
195
|
+
sp-rag token add --token <grc_pat_...>
|
|
167
196
|
sp-rag token verify --token <grc_pat_...>
|
|
168
197
|
sp-rag config show
|
|
169
198
|
sp-rag codegraph status
|
|
@@ -171,12 +200,12 @@ sp-rag codegraph watch --interval-ms 2000
|
|
|
171
200
|
sp-rag codegraph runs --limit 5
|
|
172
201
|
sp-rag codegraph metrics
|
|
173
202
|
sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
|
|
174
|
-
sp-rag mcp add antigravity
|
|
175
|
-
sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
|
|
176
|
-
sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
177
|
-
sp-rag skill install
|
|
178
|
-
sp-rag skill install --
|
|
179
|
-
sp-rag skill install --
|
|
203
|
+
sp-rag mcp add antigravity
|
|
204
|
+
sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
|
|
205
|
+
sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
206
|
+
sp-rag skill install --client codex
|
|
207
|
+
sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
208
|
+
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
180
209
|
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
181
210
|
```
|
|
182
211
|
|
|
@@ -184,7 +213,9 @@ sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
|
184
213
|
|
|
185
214
|
```bash
|
|
186
215
|
sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
|
|
187
|
-
sp-rag
|
|
216
|
+
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
217
|
+
sp-rag explain --client codex
|
|
218
|
+
sp-rag token add --token <grc_pat_...>
|
|
188
219
|
sp-rag token verify --token <grc_pat_...>
|
|
189
220
|
sp-rag config show
|
|
190
221
|
sp-rag doctor
|
|
@@ -195,13 +226,13 @@ sp-rag codegraph metrics
|
|
|
195
226
|
sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
|
|
196
227
|
sp-rag codegraph sync --branch master --commit-sha <sha> --webhook-token <token webhook codegraph> --gitlab-job-token <ci-job-token>
|
|
197
228
|
sp-rag docs get public --format md
|
|
198
|
-
sp-rag mcp add codex
|
|
199
|
-
sp-rag mcp add antigravity
|
|
200
|
-
sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
|
|
201
|
-
sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
202
|
-
sp-rag skill install
|
|
203
|
-
sp-rag skill install --
|
|
204
|
-
sp-rag skill install --
|
|
229
|
+
sp-rag mcp add codex
|
|
230
|
+
sp-rag mcp add antigravity
|
|
231
|
+
sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
|
|
232
|
+
sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
233
|
+
sp-rag skill install --client codex
|
|
234
|
+
sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
235
|
+
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
205
236
|
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
206
237
|
sp-rag update setup --client codex
|
|
207
238
|
```
|
package/dist/cli.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
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
|
-
import { defaultBaseUrl, defaultMcpServerAlias, defaultMcpUrl, installMcpConfig, } from './lib/mcp-config.js';
|
|
4
|
+
import { defaultBaseUrl, defaultMcpServerAlias, defaultMcpUrl, installMcpConfig, resolveMcpConfigPath, } from './lib/mcp-config.js';
|
|
5
5
|
import { fetchJson, fetchText, runDoctor } from './lib/http.js';
|
|
6
|
-
import { installSkill, } from './lib/skill.js';
|
|
6
|
+
import { installSkill, resolveSkillInstallTarget, } from './lib/skill.js';
|
|
7
7
|
function parseArgv(argv) {
|
|
8
8
|
const positionals = [];
|
|
9
9
|
const options = {};
|
|
@@ -111,8 +111,11 @@ async function loadRuntimeDefaults(parsed) {
|
|
|
111
111
|
const docsUrl = optionString(parsed, 'docs-url') ??
|
|
112
112
|
config?.docsUrl ??
|
|
113
113
|
`${baseUrl.replace(/\/+$/, '')}/codegraph/docs/public?format=md`;
|
|
114
|
-
const
|
|
114
|
+
const explicitClient = supportedClient(optionString(parsed, 'client'));
|
|
115
|
+
const defaultClient = explicitClient ?? supportedClient(config?.defaultClient);
|
|
115
116
|
const defaultScope = supportedScope(optionString(parsed, 'scope') ?? config?.defaultScope);
|
|
117
|
+
const explicitSkillClient = supportedSkillClient(optionString(parsed, 'skill-client'));
|
|
118
|
+
const derivedSkillClient = defaultSkillClientForMcpClient(defaultClient);
|
|
116
119
|
return {
|
|
117
120
|
config,
|
|
118
121
|
homeDir,
|
|
@@ -127,21 +130,58 @@ async function loadRuntimeDefaults(parsed) {
|
|
|
127
130
|
optionString(parsed, 'token') ??
|
|
128
131
|
process.env['SP_RAG_MCP_TOKEN']?.trim() ??
|
|
129
132
|
config?.mcpToken,
|
|
130
|
-
skillClient:
|
|
133
|
+
skillClient: explicitSkillClient ??
|
|
134
|
+
(explicitClient ? defaultSkillClientForMcpClient(explicitClient) : undefined) ??
|
|
131
135
|
config?.skillClient ??
|
|
132
|
-
|
|
136
|
+
derivedSkillClient,
|
|
133
137
|
skillTargetDir: optionString(parsed, 'target-dir') ?? config?.skillTargetDir,
|
|
134
138
|
};
|
|
135
139
|
}
|
|
140
|
+
function resolveSelectedClient(parsed, defaults, explicitClient) {
|
|
141
|
+
return supportedClient(explicitClient ??
|
|
142
|
+
optionString(parsed, 'client') ??
|
|
143
|
+
parsed.positionals[2] ??
|
|
144
|
+
defaults.defaultClient);
|
|
145
|
+
}
|
|
146
|
+
function resolveSelectedSkillClient(parsed, defaults, client) {
|
|
147
|
+
return (supportedSkillClient(optionString(parsed, 'client')) ??
|
|
148
|
+
supportedSkillClient(optionString(parsed, 'skill-client')) ??
|
|
149
|
+
(client ? defaultSkillClientForMcpClient(client) : undefined) ??
|
|
150
|
+
defaults.skillClient);
|
|
151
|
+
}
|
|
152
|
+
function deriveDefaultsForClient(parsed, defaults, client) {
|
|
153
|
+
const nextClient = client ?? defaults.defaultClient;
|
|
154
|
+
const nextScope = supportedScope(optionString(parsed, 'scope')) ?? defaults.defaultScope;
|
|
155
|
+
const nextSkillClient = resolveSelectedSkillClient(parsed, defaults, nextClient);
|
|
156
|
+
return {
|
|
157
|
+
...defaults,
|
|
158
|
+
defaultClient: nextClient,
|
|
159
|
+
defaultScope: nextScope,
|
|
160
|
+
skillClient: nextSkillClient,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function resolveAuthForMcp(parsed, defaults) {
|
|
164
|
+
const authToken = optionString(parsed, 'mcp-token') ?? defaults.mcpToken;
|
|
165
|
+
const authEnvVar = optionString(parsed, 'auth-env-var') ?? defaults.authEnvVar;
|
|
166
|
+
if (!authToken?.trim() && !authEnvVar?.trim()) {
|
|
167
|
+
throw new Error('Chưa có token MCP trong config. Hãy chạy sp-rag install --client <client> --mcp-token <token> hoặc sp-rag token add --token <token>.');
|
|
168
|
+
}
|
|
169
|
+
return {
|
|
170
|
+
authToken: authToken?.trim(),
|
|
171
|
+
authEnvVar: authEnvVar?.trim(),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
136
174
|
function helpText() {
|
|
137
175
|
return `sp-rag - CLI cho setup, MCP, codegraph, eval và skill của SP-RAG
|
|
138
176
|
|
|
139
177
|
Lệnh chính:
|
|
140
|
-
sp-rag install
|
|
178
|
+
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]
|
|
179
|
+
sp-rag add --client codex|cursor|claude-code|antigravity|vscode|opencode [--scope global|project] [--cwd PATH] [--target-dir PATH]
|
|
141
180
|
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]
|
|
142
|
-
sp-rag token add --token TOKEN
|
|
181
|
+
sp-rag token add --token TOKEN
|
|
143
182
|
sp-rag token verify [--token TOKEN] [--base-url URL]
|
|
144
183
|
sp-rag config show
|
|
184
|
+
sp-rag explain [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH]
|
|
145
185
|
sp-rag doctor [--base-url URL]
|
|
146
186
|
sp-rag codegraph status [--base-url URL]
|
|
147
187
|
sp-rag codegraph watch [--base-url URL] [--interval-ms N] [--max-polls N]
|
|
@@ -151,7 +191,7 @@ Lệnh chính:
|
|
|
151
191
|
sp-rag codegraph sync [--base-url URL] [--branch BRANCH] [--commit-sha SHA] [--force] [--webhook-token TOKEN] [--gitlab-job-token TOKEN]
|
|
152
192
|
sp-rag docs get <public|function|dev> [--base-url URL] [--format md|json|html]
|
|
153
193
|
sp-rag mcp add <codex|cursor|claude-code|antigravity|vscode|opencode> [--url URL] [--scope global|project] [--auth-env-var ENV_VAR] [--mcp-token TOKEN]
|
|
154
|
-
sp-rag skill install [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH] [--target-dir PATH] [--mcp-url URL] [--docs-url URL]
|
|
194
|
+
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]
|
|
155
195
|
sp-rag eval run --file eval-suite.json [--base-url URL]
|
|
156
196
|
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]
|
|
157
197
|
`;
|
|
@@ -296,7 +336,7 @@ async function runDocsGet(parsed) {
|
|
|
296
336
|
process.stdout.write(`${result}${result.endsWith('\n') ? '' : '\n'}`);
|
|
297
337
|
}
|
|
298
338
|
async function runMcpAdd(parsed, defaults, explicitClient) {
|
|
299
|
-
const client =
|
|
339
|
+
const client = resolveSelectedClient(parsed, defaults, explicitClient);
|
|
300
340
|
if (!client) {
|
|
301
341
|
throw new Error('Thiếu client. Dùng codex, cursor, claude-code, antigravity, vscode hoặc opencode.');
|
|
302
342
|
}
|
|
@@ -304,17 +344,15 @@ async function runMcpAdd(parsed, defaults, explicitClient) {
|
|
|
304
344
|
client,
|
|
305
345
|
url: optionString(parsed, 'url') ?? defaults.mcpUrl,
|
|
306
346
|
scope: supportedScope(optionString(parsed, 'scope')) ?? defaults.defaultScope,
|
|
307
|
-
authEnvVar:
|
|
308
|
-
authToken:
|
|
347
|
+
authEnvVar: resolveAuthForMcp(parsed, defaults).authEnvVar,
|
|
348
|
+
authToken: resolveAuthForMcp(parsed, defaults).authToken,
|
|
309
349
|
cwd: optionString(parsed, 'cwd'),
|
|
310
350
|
serverAlias: optionString(parsed, 'server-alias') ?? defaults.serverAlias,
|
|
311
351
|
});
|
|
312
352
|
process.stdout.write(`Đã cập nhật cấu hình MCP cho ${result.client} tại ${result.path} (${result.scope}).\n`);
|
|
313
353
|
}
|
|
314
|
-
async function runSkillInstall(parsed, defaults) {
|
|
315
|
-
const client =
|
|
316
|
-
defaults.skillClient ??
|
|
317
|
-
'codex';
|
|
354
|
+
async function runSkillInstall(parsed, defaults, explicitClient) {
|
|
355
|
+
const client = resolveSelectedSkillClient(parsed, defaults, explicitClient) ?? 'codex';
|
|
318
356
|
const result = await installSkill({
|
|
319
357
|
client,
|
|
320
358
|
cwd: optionString(parsed, 'cwd'),
|
|
@@ -326,26 +364,60 @@ async function runSkillInstall(parsed, defaults) {
|
|
|
326
364
|
});
|
|
327
365
|
process.stdout.write(`Đã cài skill cho ${result.client} tại ${result.path}\n`);
|
|
328
366
|
}
|
|
367
|
+
async function saveResolvedConfig(defaults) {
|
|
368
|
+
return saveCliConfig(buildCliConfig(defaults), defaults.homeDir);
|
|
369
|
+
}
|
|
370
|
+
async function runClientSetup(parsed, defaults, client) {
|
|
371
|
+
const nextDefaults = deriveDefaultsForClient(parsed, defaults, client);
|
|
372
|
+
const configPath = await saveResolvedConfig(nextDefaults);
|
|
373
|
+
process.stdout.write(`Đã lưu config CLI tại ${configPath}\n`);
|
|
374
|
+
if (!optionFlag(parsed, 'skip-mcp')) {
|
|
375
|
+
await runMcpAdd(parsed, nextDefaults, client);
|
|
376
|
+
}
|
|
377
|
+
if (!optionFlag(parsed, 'skip-skill')) {
|
|
378
|
+
await runSkillInstall(parsed, nextDefaults, client);
|
|
379
|
+
}
|
|
380
|
+
if (optionFlag(parsed, 'doctor')) {
|
|
381
|
+
const results = await runDoctor({ baseUrl: nextDefaults.baseUrl });
|
|
382
|
+
for (const result of results) {
|
|
383
|
+
process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
329
387
|
async function runInit(parsed) {
|
|
330
388
|
const defaults = await loadRuntimeDefaults(parsed);
|
|
331
|
-
const
|
|
332
|
-
const
|
|
389
|
+
const client = resolveSelectedClient(parsed, defaults);
|
|
390
|
+
const nextDefaults = deriveDefaultsForClient(parsed, defaults, client);
|
|
391
|
+
const configPath = await saveResolvedConfig(nextDefaults);
|
|
333
392
|
process.stdout.write(`Đã lưu config CLI tại ${configPath}\n`);
|
|
334
|
-
if (!optionFlag(parsed, 'skip-mcp') &&
|
|
335
|
-
await runMcpAdd(parsed,
|
|
393
|
+
if (!optionFlag(parsed, 'skip-mcp') && client) {
|
|
394
|
+
await runMcpAdd(parsed, nextDefaults, client);
|
|
336
395
|
}
|
|
337
|
-
if (!optionFlag(parsed, 'skip-skill') &&
|
|
338
|
-
await runSkillInstall(parsed,
|
|
396
|
+
if (!optionFlag(parsed, 'skip-skill') && nextDefaults.skillClient) {
|
|
397
|
+
await runSkillInstall(parsed, nextDefaults, client);
|
|
339
398
|
}
|
|
340
399
|
if (optionFlag(parsed, 'doctor')) {
|
|
341
|
-
const results = await runDoctor({ baseUrl:
|
|
400
|
+
const results = await runDoctor({ baseUrl: nextDefaults.baseUrl });
|
|
342
401
|
for (const result of results) {
|
|
343
402
|
process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
|
|
344
403
|
}
|
|
345
404
|
}
|
|
346
405
|
}
|
|
347
406
|
async function runInstall(parsed) {
|
|
348
|
-
await
|
|
407
|
+
const defaults = await loadRuntimeDefaults(parsed);
|
|
408
|
+
const client = resolveSelectedClient(parsed, defaults);
|
|
409
|
+
if (!client) {
|
|
410
|
+
throw new Error('Thiếu client. Dùng sp-rag install --client <client> --mcp-token <token>.');
|
|
411
|
+
}
|
|
412
|
+
await runClientSetup(parsed, defaults, client);
|
|
413
|
+
}
|
|
414
|
+
async function runAdd(parsed) {
|
|
415
|
+
const defaults = await loadRuntimeDefaults(parsed);
|
|
416
|
+
const client = resolveSelectedClient(parsed, defaults);
|
|
417
|
+
if (!client) {
|
|
418
|
+
throw new Error('Thiếu client. Dùng sp-rag add --client <client>.');
|
|
419
|
+
}
|
|
420
|
+
await runClientSetup(parsed, defaults, client);
|
|
349
421
|
}
|
|
350
422
|
async function runTokenAdd(parsed) {
|
|
351
423
|
const defaults = await loadRuntimeDefaults(parsed);
|
|
@@ -357,24 +429,9 @@ async function runTokenAdd(parsed) {
|
|
|
357
429
|
...defaults,
|
|
358
430
|
mcpToken: token.trim(),
|
|
359
431
|
};
|
|
360
|
-
const configPath = await
|
|
432
|
+
const configPath = await saveResolvedConfig(nextDefaults);
|
|
361
433
|
process.stdout.write(`Đã lưu token MCP tại ${configPath}\n`);
|
|
362
|
-
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
const client = supportedClient(optionString(parsed, 'client') ?? defaults.defaultClient);
|
|
366
|
-
if (!client) {
|
|
367
|
-
process.stdout.write('Chưa có client mặc định để cập nhật MCP config. Dùng thêm --client nếu muốn ghi ngay.\n');
|
|
368
|
-
return;
|
|
369
|
-
}
|
|
370
|
-
await runMcpAdd({
|
|
371
|
-
...parsed,
|
|
372
|
-
positionals: ['mcp', 'add', client],
|
|
373
|
-
options: {
|
|
374
|
-
...parsed.options,
|
|
375
|
-
'mcp-token': token.trim(),
|
|
376
|
-
},
|
|
377
|
-
}, nextDefaults, client);
|
|
434
|
+
return;
|
|
378
435
|
}
|
|
379
436
|
async function runTokenVerify(parsed) {
|
|
380
437
|
const defaults = await loadRuntimeDefaults(parsed);
|
|
@@ -395,6 +452,73 @@ async function runConfigShow(parsed) {
|
|
|
395
452
|
const config = await loadCliConfig(optionString(parsed, 'home-dir'));
|
|
396
453
|
process.stdout.write(`${JSON.stringify(config ?? {}, null, 2)}\n`);
|
|
397
454
|
}
|
|
455
|
+
async function runExplain(parsed) {
|
|
456
|
+
const defaults = await loadRuntimeDefaults(parsed);
|
|
457
|
+
const client = resolveSelectedClient(parsed, defaults);
|
|
458
|
+
const scope = supportedScope(optionString(parsed, 'scope')) ?? defaults.defaultScope;
|
|
459
|
+
const tokenStatus = defaults.mcpToken?.trim()
|
|
460
|
+
? 'đã lưu'
|
|
461
|
+
: defaults.authEnvVar?.trim()
|
|
462
|
+
? `dùng biến môi trường ${defaults.authEnvVar}`
|
|
463
|
+
: 'chưa có';
|
|
464
|
+
const lines = [];
|
|
465
|
+
lines.push('## Cấu hình hiện tại');
|
|
466
|
+
lines.push(`- Base URL: ${defaults.baseUrl}`);
|
|
467
|
+
lines.push(`- MCP URL: ${defaults.mcpUrl}`);
|
|
468
|
+
lines.push(`- Client mặc định: ${defaults.defaultClient ?? 'chưa có'}`);
|
|
469
|
+
lines.push(`- Scope mặc định: ${defaults.defaultScope ?? 'chưa có'}`);
|
|
470
|
+
lines.push(`- Token MCP đang ở trạng thái: ${tokenStatus}`);
|
|
471
|
+
if (client) {
|
|
472
|
+
try {
|
|
473
|
+
const mcpTarget = resolveMcpConfigPath({
|
|
474
|
+
client,
|
|
475
|
+
url: defaults.mcpUrl,
|
|
476
|
+
scope,
|
|
477
|
+
cwd: optionString(parsed, 'cwd'),
|
|
478
|
+
});
|
|
479
|
+
lines.push(`- File MCP cho ${client}: ${mcpTarget.path}`);
|
|
480
|
+
}
|
|
481
|
+
catch (error) {
|
|
482
|
+
lines.push(`- File MCP cho ${client}: không xác định được (${error instanceof Error ? error.message : String(error)})`);
|
|
483
|
+
}
|
|
484
|
+
try {
|
|
485
|
+
const skillClient = resolveSelectedSkillClient(parsed, defaults, client) ??
|
|
486
|
+
defaultSkillClientForMcpClient(client);
|
|
487
|
+
if (skillClient) {
|
|
488
|
+
const skillTarget = resolveSkillInstallTarget({
|
|
489
|
+
client: skillClient,
|
|
490
|
+
scope,
|
|
491
|
+
cwd: optionString(parsed, 'cwd'),
|
|
492
|
+
targetDir: optionString(parsed, 'target-dir') ?? defaults.skillTargetDir,
|
|
493
|
+
serverAlias: defaults.serverAlias,
|
|
494
|
+
mcpUrl: defaults.mcpUrl,
|
|
495
|
+
docsUrl: defaults.docsUrl,
|
|
496
|
+
});
|
|
497
|
+
lines.push(`- File skill cho ${client}: ${skillTarget.path}`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
lines.push(`- File skill cho ${client}: không xác định được (${error instanceof Error ? error.message : String(error)})`);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
lines.push('');
|
|
505
|
+
lines.push('## Cách dùng chuẩn');
|
|
506
|
+
if (!defaults.mcpToken?.trim() && !defaults.authEnvVar?.trim()) {
|
|
507
|
+
lines.push('- Máy này chưa có token MCP. Hãy chạy:');
|
|
508
|
+
lines.push(` sp-rag install --client ${client ?? 'vscode'} --mcp-token <token>`);
|
|
509
|
+
}
|
|
510
|
+
else {
|
|
511
|
+
const chosenClient = client ?? defaults.defaultClient ?? 'vscode';
|
|
512
|
+
const chosenSkillClient = resolveSelectedSkillClient(parsed, defaults, client) ?? chosenClient;
|
|
513
|
+
lines.push('- Nếu muốn cài đầy đủ MCP + skill cho client mới, hãy chạy:');
|
|
514
|
+
lines.push(` sp-rag add --client ${chosenClient}`);
|
|
515
|
+
lines.push('- Nếu chỉ muốn cập nhật MCP, hãy chạy:');
|
|
516
|
+
lines.push(` sp-rag mcp add ${chosenClient}`);
|
|
517
|
+
lines.push('- Nếu chỉ muốn cài lại skill, hãy chạy:');
|
|
518
|
+
lines.push(` sp-rag skill install --client ${chosenSkillClient}`);
|
|
519
|
+
}
|
|
520
|
+
process.stdout.write(`${lines.join('\n')}\n`);
|
|
521
|
+
}
|
|
398
522
|
async function runEval(parsed) {
|
|
399
523
|
const filePath = optionString(parsed, 'file');
|
|
400
524
|
if (!filePath) {
|
|
@@ -412,15 +536,15 @@ async function runEval(parsed) {
|
|
|
412
536
|
}
|
|
413
537
|
async function runUpdateSetup(parsed) {
|
|
414
538
|
const defaults = await loadRuntimeDefaults(parsed);
|
|
415
|
-
const client =
|
|
539
|
+
const client = resolveSelectedClient(parsed, defaults);
|
|
416
540
|
if (client) {
|
|
417
541
|
await runMcpAdd({
|
|
418
542
|
...parsed,
|
|
419
543
|
positionals: ['mcp', 'add', client],
|
|
420
544
|
}, defaults, client);
|
|
421
545
|
}
|
|
422
|
-
if (!optionFlag(parsed, 'skip-skill') && defaults
|
|
423
|
-
await runSkillInstall(parsed, defaults);
|
|
546
|
+
if (!optionFlag(parsed, 'skip-skill') && resolveSelectedSkillClient(parsed, defaults, client)) {
|
|
547
|
+
await runSkillInstall(parsed, defaults, client);
|
|
424
548
|
}
|
|
425
549
|
}
|
|
426
550
|
export async function runCli(argv) {
|
|
@@ -435,6 +559,10 @@ export async function runCli(argv) {
|
|
|
435
559
|
await runInstall(parsed);
|
|
436
560
|
return 0;
|
|
437
561
|
}
|
|
562
|
+
if (group === 'add') {
|
|
563
|
+
await runAdd(parsed);
|
|
564
|
+
return 0;
|
|
565
|
+
}
|
|
438
566
|
if (group === 'init') {
|
|
439
567
|
await runInit(parsed);
|
|
440
568
|
return 0;
|
|
@@ -451,6 +579,10 @@ export async function runCli(argv) {
|
|
|
451
579
|
await runConfigShow(parsed);
|
|
452
580
|
return 0;
|
|
453
581
|
}
|
|
582
|
+
if (group === 'explain') {
|
|
583
|
+
await runExplain(parsed);
|
|
584
|
+
return 0;
|
|
585
|
+
}
|
|
454
586
|
if (group === 'doctor') {
|
|
455
587
|
const defaults = await loadRuntimeDefaults(parsed);
|
|
456
588
|
const results = await runDoctor({
|
|
@@ -511,7 +643,7 @@ export async function runCli(argv) {
|
|
|
511
643
|
return 0;
|
|
512
644
|
}
|
|
513
645
|
if (group === 'version') {
|
|
514
|
-
process.stdout.write('sp-rag 0.6.
|
|
646
|
+
process.stdout.write('sp-rag 0.6.2\n');
|
|
515
647
|
return 0;
|
|
516
648
|
}
|
|
517
649
|
process.stdout.write(helpText());
|
package/dist/lib/mcp-config.js
CHANGED
|
@@ -19,6 +19,13 @@ function withHeaders(target, headers) {
|
|
|
19
19
|
headers,
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
|
+
function normalizeEndpoint(value) {
|
|
23
|
+
if (typeof value !== 'string') {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
const trimmed = value.trim();
|
|
27
|
+
return trimmed ? trimmed.replace(/\/+$/, '') : null;
|
|
28
|
+
}
|
|
22
29
|
function bearerHeader(authToken, authEnvVar, envStyle = 'shell') {
|
|
23
30
|
const trimmedToken = authToken?.trim();
|
|
24
31
|
if (trimmedToken) {
|
|
@@ -55,6 +62,29 @@ function upsertObjectEntry(base, sectionKey, entryKey, entryValue) {
|
|
|
55
62
|
[sectionKey]: section,
|
|
56
63
|
};
|
|
57
64
|
}
|
|
65
|
+
function upsertObjectEntryPreservingAliasByEndpoint(base, sectionKey, entryKey, entryValue, endpointKey) {
|
|
66
|
+
const section = base[sectionKey] && typeof base[sectionKey] === 'object'
|
|
67
|
+
? { ...base[sectionKey] }
|
|
68
|
+
: {};
|
|
69
|
+
const targetEndpoint = normalizeEndpoint(entryValue[endpointKey]);
|
|
70
|
+
const existingAlias = targetEndpoint
|
|
71
|
+
? Object.entries(section).find(([alias, value]) => {
|
|
72
|
+
if (alias === entryKey || !value || typeof value !== 'object') {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
return normalizeEndpoint(value[endpointKey]) === targetEndpoint;
|
|
76
|
+
})?.[0]
|
|
77
|
+
: undefined;
|
|
78
|
+
const aliasToWrite = existingAlias ?? entryKey;
|
|
79
|
+
section[aliasToWrite] = entryValue;
|
|
80
|
+
if (existingAlias && existingAlias !== entryKey && entryKey in section) {
|
|
81
|
+
delete section[entryKey];
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
...base,
|
|
85
|
+
[sectionKey]: section,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
58
88
|
function vscodeGlobalConfigPath(home) {
|
|
59
89
|
if (process.platform === 'win32') {
|
|
60
90
|
const appData = process.env['APPDATA']?.trim() || path.join(home, 'AppData', 'Roaming');
|
|
@@ -98,14 +128,14 @@ export function upsertJsonMcpConfig(existing, options) {
|
|
|
98
128
|
...(options.includeTypeHttp ? { type: 'http' } : {}),
|
|
99
129
|
url: options.url,
|
|
100
130
|
}, bearerHeader(options.authToken, options.authEnvVar, 'shell'));
|
|
101
|
-
return `${JSON.stringify(
|
|
131
|
+
return `${JSON.stringify(upsertObjectEntryPreservingAliasByEndpoint(base, 'mcpServers', options.serverAlias, serverConfig, 'url'), null, 2)}\n`;
|
|
102
132
|
}
|
|
103
133
|
export function upsertAntigravityConfig(existing, options) {
|
|
104
134
|
const base = parseJsonObject(existing);
|
|
105
135
|
const serverConfig = withHeaders({
|
|
106
136
|
serverUrl: options.url,
|
|
107
137
|
}, bearerHeader(options.authToken, options.authEnvVar, 'shell'));
|
|
108
|
-
return `${JSON.stringify(
|
|
138
|
+
return `${JSON.stringify(upsertObjectEntryPreservingAliasByEndpoint(base, 'mcpServers', options.serverAlias, serverConfig, 'serverUrl'), null, 2)}\n`;
|
|
109
139
|
}
|
|
110
140
|
export function upsertVsCodeConfig(existing, options) {
|
|
111
141
|
const base = parseJsonObject(existing);
|
|
@@ -113,7 +143,7 @@ export function upsertVsCodeConfig(existing, options) {
|
|
|
113
143
|
type: 'http',
|
|
114
144
|
url: options.url,
|
|
115
145
|
}, bearerHeader(options.authToken, options.authEnvVar, 'vscode'));
|
|
116
|
-
return `${JSON.stringify(
|
|
146
|
+
return `${JSON.stringify(upsertObjectEntryPreservingAliasByEndpoint(base, 'servers', options.serverAlias, serverConfig, 'url'), null, 2)}\n`;
|
|
117
147
|
}
|
|
118
148
|
export function upsertOpenCodeConfig(existing, options) {
|
|
119
149
|
const base = parseJsonObject(existing);
|
|
@@ -130,7 +160,7 @@ export function upsertOpenCodeConfig(existing, options) {
|
|
|
130
160
|
enabled: true,
|
|
131
161
|
...(headers ? { oauth: false } : {}),
|
|
132
162
|
}, headers);
|
|
133
|
-
return `${JSON.stringify(
|
|
163
|
+
return `${JSON.stringify(upsertObjectEntryPreservingAliasByEndpoint(nextBase, 'mcp', options.serverAlias, serverConfig, 'url'), null, 2)}\n`;
|
|
134
164
|
}
|
|
135
165
|
export function resolveMcpConfigPath(options) {
|
|
136
166
|
const inferredScope = options.client === 'codex' || options.client === 'antigravity'
|
package/dist/lib/skill.js
CHANGED
|
@@ -156,6 +156,22 @@ function renderSkillArtifact(context) {
|
|
|
156
156
|
};
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
|
+
export function resolveSkillInstallTarget(options = {}) {
|
|
160
|
+
const client = options.client ?? 'codex';
|
|
161
|
+
const scope = normalizeScope(client, options.scope);
|
|
162
|
+
const targetDir = path.resolve(options.targetDir ?? defaultSkillDir(client, options.cwd, scope));
|
|
163
|
+
const artifact = renderSkillArtifact({
|
|
164
|
+
client,
|
|
165
|
+
serverAlias: options.serverAlias?.trim() || 'sp-rag',
|
|
166
|
+
mcpUrl: options.mcpUrl?.trim() || 'https://sp-rag.secomapp.com/mcp',
|
|
167
|
+
docsUrl: options.docsUrl?.trim() || 'https://sp-rag.secomapp.com/codegraph/docs/public?format=md',
|
|
168
|
+
});
|
|
169
|
+
return {
|
|
170
|
+
client,
|
|
171
|
+
scope,
|
|
172
|
+
path: path.join(targetDir, artifact.fileName),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
159
175
|
export function renderSkill(options) {
|
|
160
176
|
return renderSkillArtifact(options).content;
|
|
161
177
|
}
|
|
@@ -166,21 +182,18 @@ export function renderCodexSkill(options) {
|
|
|
166
182
|
});
|
|
167
183
|
}
|
|
168
184
|
export async function installSkill(options = {}) {
|
|
169
|
-
const
|
|
170
|
-
const scope = normalizeScope(client, options.scope);
|
|
171
|
-
const targetDir = path.resolve(options.targetDir ?? defaultSkillDir(client, options.cwd, scope));
|
|
185
|
+
const resolved = resolveSkillInstallTarget(options);
|
|
172
186
|
const artifact = renderSkillArtifact({
|
|
173
|
-
client,
|
|
187
|
+
client: resolved.client,
|
|
174
188
|
serverAlias: options.serverAlias?.trim() || 'sp-rag',
|
|
175
189
|
mcpUrl: options.mcpUrl?.trim() || 'https://sp-rag.secomapp.com/mcp',
|
|
176
190
|
docsUrl: options.docsUrl?.trim() || 'https://sp-rag.secomapp.com/codegraph/docs/public?format=md',
|
|
177
191
|
});
|
|
178
|
-
|
|
179
|
-
await
|
|
180
|
-
await writeFile(filePath, artifact.content, 'utf8');
|
|
192
|
+
await mkdir(path.dirname(resolved.path), { recursive: true });
|
|
193
|
+
await writeFile(resolved.path, artifact.content, 'utf8');
|
|
181
194
|
return {
|
|
182
|
-
client,
|
|
183
|
-
path:
|
|
195
|
+
client: resolved.client,
|
|
196
|
+
path: resolved.path,
|
|
184
197
|
};
|
|
185
198
|
}
|
|
186
199
|
export async function installCodexSkill(options = {}) {
|