@getvetai/cli 0.1.0 โ 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +51 -34
- package/dist/commands/find.d.ts +2 -1
- package/dist/commands/find.js +3 -9
- package/dist/commands/scan.d.ts +2 -0
- package/dist/commands/scan.js +56 -1
- package/dist/index.js +5 -2
- package/dist/utils/api.d.ts +6 -0
- package/dist/utils/api.js +50 -0
- package/dist/utils/config.js +54 -0
- package/dist/utils/display.d.ts +5 -1
- package/dist/utils/display.js +11 -3
- package/package.json +1 -7
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @getvetai/cli
|
|
2
2
|
|
|
3
|
-
Security audit CLI for AI skills and MCP servers. Scan, audit, and
|
|
3
|
+
Security audit CLI for AI skills and MCP servers. Scan, audit, and discover tools before you install them.
|
|
4
4
|
|
|
5
|
-
๐ **Registry:** [getvet.ai](https://getvet.ai) โ
|
|
5
|
+
๐ **Registry:** [getvet.ai](https://getvet.ai) โ 20,000+ AI tools cataloged and scored
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -10,31 +10,49 @@ Security audit CLI for AI skills and MCP servers. Scan, audit, and score tools b
|
|
|
10
10
|
npm install -g @getvetai/cli
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
Or run without installing:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx @getvetai/cli scan .
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## What's New in v0.3.0
|
|
20
|
+
|
|
21
|
+
- **`vet find --limit <n>`** โ control how many results to return (default: 10, max: 48)
|
|
22
|
+
- **`vet find --type <type>`** โ filter by `skill`, `mcp`, or `all`
|
|
23
|
+
- **20,000+ tools** in the registry (up from 12K) โ now indexing 10 sources including Smithery, mcp.so, MCP Registry, PyPI, npm, GitHub, and more
|
|
24
|
+
|
|
13
25
|
## Commands
|
|
14
26
|
|
|
15
27
|
### `vet scan <target>`
|
|
16
28
|
|
|
17
|
-
Scan a
|
|
29
|
+
Scan a tool for security issues. Checks the [getvet.ai](https://getvet.ai) registry first for instant results.
|
|
18
30
|
|
|
19
31
|
```bash
|
|
20
|
-
# Scan
|
|
21
|
-
vet scan ./my-skill/SKILL.md
|
|
22
|
-
|
|
23
|
-
# Scan an npm package
|
|
32
|
+
# Scan an npm package (checks registry first)
|
|
24
33
|
vet scan @modelcontextprotocol/server-filesystem
|
|
25
34
|
|
|
35
|
+
# Local analysis only (skip registry)
|
|
36
|
+
vet scan @modelcontextprotocol/server-filesystem --offline
|
|
37
|
+
|
|
38
|
+
# Request a deep scan from registry
|
|
39
|
+
vet scan @modelcontextprotocol/server-filesystem --deep
|
|
40
|
+
|
|
41
|
+
# Scan a local project
|
|
42
|
+
vet scan ./my-mcp-server
|
|
43
|
+
|
|
26
44
|
# Scan a GitHub repo
|
|
27
45
|
vet scan https://github.com/modelcontextprotocol/servers
|
|
28
46
|
|
|
29
|
-
#
|
|
47
|
+
# JSON output
|
|
30
48
|
vet scan ./SKILL.md --json
|
|
31
49
|
```
|
|
32
50
|
|
|
33
|
-
**Output includes:** trust score, badge (certified/reviewed/unverified/flagged), detected permissions, security issues, risk factors, and tools list.
|
|
34
|
-
|
|
35
51
|
### `vet audit [path]`
|
|
36
52
|
|
|
37
|
-
Audit all AI tools in a project.
|
|
53
|
+
Audit all AI tools in a project. Auto-discovers MCP configurations from:
|
|
54
|
+
|
|
55
|
+
**Claude Desktop** ยท **Cursor** ยท **VS Code** ยท **Windsurf** ยท **Cline** ยท **Zed** ยท **Continue** ยท **OpenClaw**
|
|
38
56
|
|
|
39
57
|
```bash
|
|
40
58
|
# Audit current directory
|
|
@@ -43,7 +61,7 @@ vet audit
|
|
|
43
61
|
# Audit a specific project
|
|
44
62
|
vet audit ./my-project
|
|
45
63
|
|
|
46
|
-
# Strict mode โ exit
|
|
64
|
+
# Strict mode โ exit 1 if any tool is unverified/flagged
|
|
47
65
|
vet audit --strict
|
|
48
66
|
|
|
49
67
|
# JSON output
|
|
@@ -52,33 +70,33 @@ vet audit --json
|
|
|
52
70
|
|
|
53
71
|
### `vet find <query>`
|
|
54
72
|
|
|
55
|
-
Search the getvet.ai registry by description.
|
|
73
|
+
Search the getvet.ai registry for tools by description.
|
|
56
74
|
|
|
57
75
|
```bash
|
|
58
76
|
# Search for tools
|
|
59
|
-
vet find "
|
|
60
|
-
vet find "database
|
|
77
|
+
vet find "web scraping"
|
|
78
|
+
vet find "database access"
|
|
79
|
+
|
|
80
|
+
# Limit results
|
|
81
|
+
vet find "browser automation" --limit 20
|
|
82
|
+
|
|
83
|
+
# Filter by type
|
|
84
|
+
vet find "file management" --type mcp
|
|
61
85
|
|
|
62
86
|
# JSON output
|
|
63
87
|
vet find "weather" --json
|
|
64
|
-
|
|
65
|
-
# Use local API
|
|
66
|
-
vet find "search" --api http://localhost:3300
|
|
67
88
|
```
|
|
68
89
|
|
|
69
90
|
### `vet install <package>`
|
|
70
91
|
|
|
71
|
-
Install a package with a pre-install security audit.
|
|
92
|
+
Install a package with a pre-install security audit.
|
|
72
93
|
|
|
73
94
|
```bash
|
|
74
|
-
# Audit + install
|
|
95
|
+
# Audit + install
|
|
75
96
|
vet install @modelcontextprotocol/server-github
|
|
76
97
|
|
|
77
98
|
# Install globally
|
|
78
99
|
vet install -g some-mcp-server
|
|
79
|
-
|
|
80
|
-
# Install as OpenClaw skill
|
|
81
|
-
vet install --skill weather
|
|
82
100
|
```
|
|
83
101
|
|
|
84
102
|
## Trust Scores
|
|
@@ -86,22 +104,21 @@ vet install --skill weather
|
|
|
86
104
|
| Score | Badge | Meaning |
|
|
87
105
|
|-------|-------|---------|
|
|
88
106
|
| 75+ | โ
Certified | No critical issues, good practices |
|
|
89
|
-
| 50
|
|
90
|
-
| 25
|
|
91
|
-
| 0
|
|
107
|
+
| 50โ74 | ๐ Reviewed | Some concerns, use with caution |
|
|
108
|
+
| 25โ49 | โ ๏ธ Unverified | Not yet reviewed or limited info |
|
|
109
|
+
| 0โ24 | ๐ซ Flagged | Critical security issues found |
|
|
92
110
|
|
|
93
111
|
## What It Detects
|
|
94
112
|
|
|
95
|
-
**Permissions:** shell execution, file
|
|
96
|
-
|
|
97
|
-
**
|
|
98
|
-
|
|
99
|
-
**MCP-specific:** tool parameter analysis, transport detection (stdio/http/sse), runtime detection, environment variable scanning.
|
|
113
|
+
- **Permissions:** shell execution, file I/O, network access, browser control, database queries, crypto operations
|
|
114
|
+
- **Security issues:** destructive commands, remote code execution, dynamic eval, credential patterns, elevated privileges
|
|
115
|
+
- **MCP-specific:** tool parameter analysis, transport detection (stdio/http/sse), runtime detection
|
|
100
116
|
|
|
101
|
-
##
|
|
117
|
+
## Links
|
|
102
118
|
|
|
103
|
-
|
|
119
|
+
- ๐ [getvet.ai](https://getvet.ai) โ Browse the registry
|
|
120
|
+
- ๐ฆ [npm](https://www.npmjs.com/package/@getvetai/cli) โ Package page
|
|
104
121
|
|
|
105
122
|
## License
|
|
106
123
|
|
|
107
|
-
MIT
|
|
124
|
+
MIT
|
package/dist/commands/find.d.ts
CHANGED
package/dist/commands/find.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import ora from 'ora';
|
|
2
|
+
import { searchTools } from '../utils/api.js';
|
|
2
3
|
import { displayFindResults } from '../utils/display.js';
|
|
3
4
|
export async function findCommand(query, options) {
|
|
4
5
|
const spinner = ora(`Searching "${query}"...`).start();
|
|
5
|
-
const api = options.api || 'https://getvet.ai';
|
|
6
6
|
try {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
throw new Error(`API returned ${r.status}`);
|
|
10
|
-
const data = await r.json();
|
|
11
|
-
// Handle both array response and { results: [...] } response
|
|
12
|
-
const items = Array.isArray(data) ? data : (data.results || []);
|
|
13
|
-
const results = items.map(x => ({
|
|
7
|
+
const items = await searchTools(query, { limit: Number(options.limit) || 10, type: options.type });
|
|
8
|
+
const results = items.map((x) => ({
|
|
14
9
|
name: x.name,
|
|
15
10
|
slug: x.slug,
|
|
16
11
|
description: x.description,
|
|
@@ -28,7 +23,6 @@ export async function findCommand(query, options) {
|
|
|
28
23
|
}
|
|
29
24
|
catch (err) {
|
|
30
25
|
spinner.fail(`Search failed: ${err.message}`);
|
|
31
|
-
console.log(' Tip: use --api http://localhost:3300 for local dev');
|
|
32
26
|
process.exitCode = 1;
|
|
33
27
|
}
|
|
34
28
|
}
|
package/dist/commands/scan.d.ts
CHANGED
package/dist/commands/scan.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { readFileSync, existsSync } from 'fs';
|
|
2
|
+
import chalk from 'chalk';
|
|
2
3
|
import ora from 'ora';
|
|
3
4
|
import { analyzeSkill } from '../utils/analyzer.js';
|
|
4
5
|
import { analyzeMcp } from '../utils/mcp-analyzer.js';
|
|
5
6
|
import { displayScanReport } from '../utils/display.js';
|
|
7
|
+
import { lookupTool, requestDeepScan } from '../utils/api.js';
|
|
6
8
|
function detect(t) {
|
|
7
9
|
if (/^https?:\/\/github\.com\//.test(t))
|
|
8
10
|
return 'github';
|
|
@@ -17,6 +19,59 @@ export async function scanCommand(target, options) {
|
|
|
17
19
|
try {
|
|
18
20
|
const tt = detect(target);
|
|
19
21
|
let result;
|
|
22
|
+
let registrySlug;
|
|
23
|
+
// Try registry lookup first for npm packages (unless --offline)
|
|
24
|
+
if (tt === 'npm' && !options.offline) {
|
|
25
|
+
spinner.text = `Checking Vet registry for ${target}...`;
|
|
26
|
+
const registryData = await lookupTool(target);
|
|
27
|
+
if (registryData && registryData.scanTier === 'deep') {
|
|
28
|
+
spinner.succeed(chalk.green('โ Found in Vet registry (deep scan available)'));
|
|
29
|
+
registrySlug = registryData.slug || target;
|
|
30
|
+
result = {
|
|
31
|
+
name: registryData.name || target,
|
|
32
|
+
description: registryData.description,
|
|
33
|
+
type: registryData.type || 'mcp',
|
|
34
|
+
transport: registryData.transport,
|
|
35
|
+
runtime: registryData.runtime,
|
|
36
|
+
tools: registryData.tools || [],
|
|
37
|
+
permissions: registryData.permissions || [],
|
|
38
|
+
issues: registryData.issues || [],
|
|
39
|
+
trustScore: registryData.trustScore ?? registryData.trust_score ?? 50,
|
|
40
|
+
badge: registryData.badge || 'unverified',
|
|
41
|
+
codeQuality: registryData.codeQuality || { hasTests: false, hasDocs: false, linesOfCode: 0 },
|
|
42
|
+
riskFactors: registryData.riskFactors || [],
|
|
43
|
+
overview: registryData.overview,
|
|
44
|
+
envVars: registryData.envVars,
|
|
45
|
+
};
|
|
46
|
+
if (options.json)
|
|
47
|
+
console.log(JSON.stringify(result, null, 2));
|
|
48
|
+
else
|
|
49
|
+
displayScanReport(result, registrySlug);
|
|
50
|
+
process.exitCode = result.badge === 'flagged' ? 1 : 0;
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (registryData) {
|
|
54
|
+
registrySlug = registryData.slug || target;
|
|
55
|
+
console.log(chalk.cyan(` โน Found in Vet registry (indexed) โ running local analysis too`));
|
|
56
|
+
console.log(chalk.gray(` View: https://getvet.ai/catalog/${registrySlug}`));
|
|
57
|
+
console.log();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Deep scan request
|
|
61
|
+
if (options.deep && tt === 'npm') {
|
|
62
|
+
spinner.text = `Requesting deep scan for ${target}...`;
|
|
63
|
+
const deepResult = await requestDeepScan(target);
|
|
64
|
+
if (deepResult) {
|
|
65
|
+
spinner.succeed(chalk.green('โ Deep scan requested'));
|
|
66
|
+
if (deepResult.status === 'queued') {
|
|
67
|
+
console.log(chalk.gray(` Deep scan queued. Check back soon at https://getvet.ai/catalog/${target}`));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
spinner.info('Deep scan request failed โ proceeding with local analysis');
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Local analysis (current behavior)
|
|
20
75
|
if (tt === 'npm') {
|
|
21
76
|
spinner.text = `Fetching npm: ${target}`;
|
|
22
77
|
result = await analyzeMcp({ npmPackage: target });
|
|
@@ -43,7 +98,7 @@ export async function scanCommand(target, options) {
|
|
|
43
98
|
if (options.json)
|
|
44
99
|
console.log(JSON.stringify(result, null, 2));
|
|
45
100
|
else
|
|
46
|
-
displayScanReport(result);
|
|
101
|
+
displayScanReport(result, registrySlug);
|
|
47
102
|
process.exitCode = result.badge === 'flagged' ? 1 : 0;
|
|
48
103
|
}
|
|
49
104
|
catch (err) {
|
package/dist/index.js
CHANGED
|
@@ -8,13 +8,15 @@ const program = new Command();
|
|
|
8
8
|
program
|
|
9
9
|
.name('vet')
|
|
10
10
|
.description('Security audit CLI for AI skills & MCP servers')
|
|
11
|
-
.version('0.
|
|
11
|
+
.version('0.2.0');
|
|
12
12
|
program
|
|
13
13
|
.command('scan')
|
|
14
14
|
.description('Scan a single tool for security issues')
|
|
15
15
|
.argument('<target>', 'URL, npm package, file path, or GitHub repo')
|
|
16
16
|
.option('--json', 'Output JSON instead of formatted report')
|
|
17
17
|
.option('--overview', 'Include AI-generated overview')
|
|
18
|
+
.option('--offline', 'Skip registry lookup')
|
|
19
|
+
.option('--deep', 'Request deep scan from registry')
|
|
18
20
|
.action(scanCommand);
|
|
19
21
|
program
|
|
20
22
|
.command('audit')
|
|
@@ -28,8 +30,9 @@ program
|
|
|
28
30
|
.command('find')
|
|
29
31
|
.description('Search for tools by description')
|
|
30
32
|
.argument('<query>', 'Natural language search query')
|
|
33
|
+
.option('--limit <n>', 'Max results to return (default: 10, max: 48)', '10')
|
|
34
|
+
.option('--type <type>', 'Filter by type: skill, mcp, or all (default: all)')
|
|
31
35
|
.option('--json', 'Output JSON')
|
|
32
|
-
.option('--api <url>', 'API endpoint (default: https://getvet.ai)')
|
|
33
36
|
.action(findCommand);
|
|
34
37
|
program
|
|
35
38
|
.command('install')
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function lookupTool(slug: string): Promise<any | null>;
|
|
2
|
+
export declare function searchTools(query: string, options?: {
|
|
3
|
+
limit?: number;
|
|
4
|
+
type?: string;
|
|
5
|
+
}): Promise<any[]>;
|
|
6
|
+
export declare function requestDeepScan(slug: string): Promise<any | null>;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
const API_BASE = 'https://getvet.ai';
|
|
2
|
+
export async function lookupTool(slug) {
|
|
3
|
+
try {
|
|
4
|
+
const resp = await fetch(`${API_BASE}/api/skills/${encodeURIComponent(slug)}`, {
|
|
5
|
+
headers: { 'User-Agent': 'vet-cli/0.3.0' },
|
|
6
|
+
signal: AbortSignal.timeout(5000),
|
|
7
|
+
});
|
|
8
|
+
if (resp.ok)
|
|
9
|
+
return await resp.json();
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export async function searchTools(query, options) {
|
|
17
|
+
try {
|
|
18
|
+
const limit = Math.min(Math.max(Number(options?.limit) || 10, 1), 48);
|
|
19
|
+
const params = new URLSearchParams({ q: query, limit: String(limit) });
|
|
20
|
+
if (options?.type && options.type !== 'all')
|
|
21
|
+
params.set('type', options.type);
|
|
22
|
+
const resp = await fetch(`${API_BASE}/api/skills/search?${params}`, {
|
|
23
|
+
headers: { 'User-Agent': 'vet-cli/0.3.0' },
|
|
24
|
+
signal: AbortSignal.timeout(5000),
|
|
25
|
+
});
|
|
26
|
+
if (resp.ok) {
|
|
27
|
+
const data = await resp.json();
|
|
28
|
+
return Array.isArray(data) ? data : data.results || data.items || [];
|
|
29
|
+
}
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return [];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function requestDeepScan(slug) {
|
|
37
|
+
try {
|
|
38
|
+
const resp = await fetch(`${API_BASE}/api/tools/${encodeURIComponent(slug)}/deep-scan`, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: { 'User-Agent': 'vet-cli/0.3.0', 'Content-Type': 'application/json' },
|
|
41
|
+
signal: AbortSignal.timeout(10000),
|
|
42
|
+
});
|
|
43
|
+
if (resp.ok)
|
|
44
|
+
return await resp.json();
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
}
|
package/dist/utils/config.js
CHANGED
|
@@ -21,11 +21,15 @@ export function discoverTools(projectPath) {
|
|
|
21
21
|
}
|
|
22
22
|
catch { /* skip */ }
|
|
23
23
|
}
|
|
24
|
+
// Standard MCP config files (mcpServers or servers key at top level)
|
|
24
25
|
const mcpPaths = [
|
|
25
26
|
join(projectPath, '.cursor', 'mcp.json'),
|
|
26
27
|
join(projectPath, 'mcp.json'),
|
|
27
28
|
join(homedir(), '.config', 'claude', 'claude_desktop_config.json'),
|
|
28
29
|
join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'),
|
|
30
|
+
join(homedir(), '.windsurf', 'mcp.json'),
|
|
31
|
+
join(homedir(), '.config', 'windsurf', 'mcp.json'),
|
|
32
|
+
join(homedir(), '.config', 'cline', 'mcp_settings.json'),
|
|
29
33
|
];
|
|
30
34
|
for (const cp of mcpPaths) {
|
|
31
35
|
if (!existsSync(cp))
|
|
@@ -43,6 +47,56 @@ export function discoverTools(projectPath) {
|
|
|
43
47
|
}
|
|
44
48
|
catch { /* skip */ }
|
|
45
49
|
}
|
|
50
|
+
// VS Code settings.json โ look for mcp.servers key
|
|
51
|
+
const vscodePath = join(homedir(), '.config', 'Code', 'User', 'settings.json');
|
|
52
|
+
if (existsSync(vscodePath)) {
|
|
53
|
+
try {
|
|
54
|
+
const cfg = JSON.parse(readFileSync(vscodePath, 'utf-8'));
|
|
55
|
+
const svrs = cfg['mcp.servers'] || cfg?.mcp?.servers || {};
|
|
56
|
+
let c = 0;
|
|
57
|
+
for (const [n, v] of Object.entries(svrs)) {
|
|
58
|
+
const target = (v.command === 'npx' ? v.args?.[0] : undefined) || n;
|
|
59
|
+
tools.push({ name: n, source: vscodePath.replace(homedir(), '~'), type: 'mcp-config', target });
|
|
60
|
+
c++;
|
|
61
|
+
}
|
|
62
|
+
add(vscodePath.replace(homedir(), '~'), c);
|
|
63
|
+
}
|
|
64
|
+
catch { /* skip */ }
|
|
65
|
+
}
|
|
66
|
+
// Zed settings.json โ look for mcp key
|
|
67
|
+
const zedPath = join(homedir(), '.config', 'zed', 'settings.json');
|
|
68
|
+
if (existsSync(zedPath)) {
|
|
69
|
+
try {
|
|
70
|
+
const cfg = JSON.parse(readFileSync(zedPath, 'utf-8'));
|
|
71
|
+
const svrs = cfg?.mcp?.servers || cfg?.mcp || {};
|
|
72
|
+
let c = 0;
|
|
73
|
+
for (const [n, v] of Object.entries(svrs)) {
|
|
74
|
+
if (typeof v !== 'object' || v === null)
|
|
75
|
+
continue;
|
|
76
|
+
const target = (v.command === 'npx' ? v.args?.[0] : undefined) || n;
|
|
77
|
+
tools.push({ name: n, source: zedPath.replace(homedir(), '~'), type: 'mcp-config', target });
|
|
78
|
+
c++;
|
|
79
|
+
}
|
|
80
|
+
add(zedPath.replace(homedir(), '~'), c);
|
|
81
|
+
}
|
|
82
|
+
catch { /* skip */ }
|
|
83
|
+
}
|
|
84
|
+
// Continue config.json โ look for mcpServers key
|
|
85
|
+
const continuePath = join(homedir(), '.continue', 'config.json');
|
|
86
|
+
if (existsSync(continuePath)) {
|
|
87
|
+
try {
|
|
88
|
+
const cfg = JSON.parse(readFileSync(continuePath, 'utf-8'));
|
|
89
|
+
const svrs = cfg.mcpServers || {};
|
|
90
|
+
let c = 0;
|
|
91
|
+
for (const [n, v] of Object.entries(svrs)) {
|
|
92
|
+
const target = (v.command === 'npx' ? v.args?.[0] : undefined) || n;
|
|
93
|
+
tools.push({ name: n, source: continuePath.replace(homedir(), '~'), type: 'mcp-config', target });
|
|
94
|
+
c++;
|
|
95
|
+
}
|
|
96
|
+
add(continuePath.replace(homedir(), '~'), c);
|
|
97
|
+
}
|
|
98
|
+
catch { /* skip */ }
|
|
99
|
+
}
|
|
46
100
|
for (const op of [join(projectPath, 'openclaw.json'), join(homedir(), '.openclaw', 'openclaw.json')]) {
|
|
47
101
|
if (!existsSync(op))
|
|
48
102
|
continue;
|
package/dist/utils/display.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import type { AnalysisResult, BadgeType } from '../types.js';
|
|
2
|
-
export declare function displayScanReport(r: AnalysisResult): void;
|
|
2
|
+
export declare function displayScanReport(r: AnalysisResult, registrySlug?: string): void;
|
|
3
3
|
export declare function displayAuditReport(results: AnalysisResult[], sources: {
|
|
4
4
|
source: string;
|
|
5
5
|
count: number;
|
|
6
6
|
}[]): void;
|
|
7
7
|
export declare function displayFindResults(results: Array<{
|
|
8
8
|
name: string;
|
|
9
|
+
slug?: string;
|
|
9
10
|
description?: string;
|
|
10
11
|
trustScore?: number;
|
|
11
12
|
badge?: BadgeType;
|
|
13
|
+
author?: string;
|
|
14
|
+
version?: string;
|
|
15
|
+
installs?: number;
|
|
12
16
|
}>): void;
|
package/dist/utils/display.js
CHANGED
|
@@ -23,7 +23,7 @@ function overallRisk(r) {
|
|
|
23
23
|
return 'medium';
|
|
24
24
|
return 'low';
|
|
25
25
|
}
|
|
26
|
-
export function displayScanReport(r) {
|
|
26
|
+
export function displayScanReport(r, registrySlug) {
|
|
27
27
|
const b = BADGE[r.badge], rk = overallRisk(r);
|
|
28
28
|
console.log();
|
|
29
29
|
console.log(chalk.bold(' ๐ Vet Security Report'));
|
|
@@ -86,6 +86,10 @@ export function displayScanReport(r) {
|
|
|
86
86
|
for (const l of r.overview.split('\n'))
|
|
87
87
|
console.log(` ${chalk.gray(l)}`);
|
|
88
88
|
}
|
|
89
|
+
if (registrySlug) {
|
|
90
|
+
console.log();
|
|
91
|
+
console.log(` ${chalk.gray('View full report:')} ${chalk.cyan(`https://getvet.ai/catalog/${registrySlug}`)}`);
|
|
92
|
+
}
|
|
89
93
|
console.log();
|
|
90
94
|
console.log(chalk.gray(' โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ'));
|
|
91
95
|
console.log();
|
|
@@ -128,10 +132,14 @@ export function displayFindResults(results) {
|
|
|
128
132
|
for (const r of results) {
|
|
129
133
|
const b = r.badge ? BADGE[r.badge] : BADGE.unverified;
|
|
130
134
|
console.log();
|
|
131
|
-
console.log(` ${b.emoji} ${chalk.bold(r.name)}`);
|
|
135
|
+
console.log(` ${b.emoji} ${chalk.bold(r.name)}${r.version ? chalk.gray(` v${r.version}`) : ''}${r.author ? chalk.gray(` by ${r.author}`) : ''}`);
|
|
132
136
|
if (r.description)
|
|
133
137
|
console.log(` ${chalk.gray(r.description.slice(0, 80))}`);
|
|
134
|
-
console.log(` Score: ${r.trustScore != null ? bar(r.trustScore) : chalk.gray('N/A')}`);
|
|
138
|
+
console.log(` Score: ${r.trustScore != null ? bar(r.trustScore) : chalk.gray('N/A')} Badge: ${b.emoji} ${b.color(b.label)}`);
|
|
139
|
+
if (r.installs != null)
|
|
140
|
+
console.log(` ${chalk.gray(`${r.installs.toLocaleString()} installs`)}`);
|
|
141
|
+
if (r.slug)
|
|
142
|
+
console.log(` ${chalk.cyan(`https://getvet.ai/catalog/${r.slug}`)}`);
|
|
135
143
|
}
|
|
136
144
|
console.log();
|
|
137
145
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getvetai/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Security audit CLI for AI skills and MCP servers โ scan, audit, and score tools before you install them",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -12,13 +12,7 @@
|
|
|
12
12
|
"README.md",
|
|
13
13
|
"LICENSE"
|
|
14
14
|
],
|
|
15
|
-
"repository": {
|
|
16
|
-
"type": "git",
|
|
17
|
-
"url": "https://github.com/getvetai/vet.git",
|
|
18
|
-
"directory": "cli"
|
|
19
|
-
},
|
|
20
15
|
"homepage": "https://getvet.ai",
|
|
21
|
-
"bugs": "https://github.com/getvetai/vet/issues",
|
|
22
16
|
"keywords": [
|
|
23
17
|
"ai",
|
|
24
18
|
"security",
|