valicode 2.0.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 +58 -0
- package/bin/valicode.js +170 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# valicode
|
|
2
|
+
|
|
3
|
+
Official npm wrapper for the Valicode CLI.
|
|
4
|
+
|
|
5
|
+
Valicode audits AI-generated code before it reaches production. The npm package uses the same Python CLI published as `valicode` on PyPI, so npm and pip users get the same scan engine and API behavior.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
- Node.js 18+
|
|
10
|
+
- Python 3.10+
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g valicode
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then run:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
valicode --version
|
|
22
|
+
valicode login
|
|
23
|
+
valicode scan .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## One-off use
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx valicode scan . --output summary
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## CI
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npx valicode scan . --fail-under 60 --format sarif > valicode.sarif
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## How it works
|
|
39
|
+
|
|
40
|
+
This package provides the `valicode` command for npm users. On first run, it checks for Python 3.10 or newer, creates an isolated Python environment under your user cache directory, and installs/updates the PyPI package with:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
python -m pip install --upgrade valicode
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then it runs the Python CLI module directly.
|
|
47
|
+
|
|
48
|
+
If automatic install is blocked by your Python environment, install Valicode manually:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
pipx install valicode
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
or:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
python3 -m pip install --user valicode
|
|
58
|
+
```
|
package/bin/valicode.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require("node:child_process");
|
|
4
|
+
const { existsSync, mkdirSync } = require("node:fs");
|
|
5
|
+
const { homedir } = require("node:os");
|
|
6
|
+
const { join } = require("node:path");
|
|
7
|
+
|
|
8
|
+
const MIN_PYTHON = { major: 3, minor: 10 };
|
|
9
|
+
const PYTHON_CANDIDATES = process.platform === "win32" ? ["py", "python3", "python"] : ["python3", "python"];
|
|
10
|
+
|
|
11
|
+
function run(command, args, options = {}) {
|
|
12
|
+
return spawnSync(command, args, {
|
|
13
|
+
encoding: "utf8",
|
|
14
|
+
stdio: options.stdio || "pipe",
|
|
15
|
+
env: process.env
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function parseVersion(output) {
|
|
20
|
+
const match = output.match(/Python\s+(\d+)\.(\d+)\.(\d+)/);
|
|
21
|
+
if (!match) return null;
|
|
22
|
+
return {
|
|
23
|
+
major: Number(match[1]),
|
|
24
|
+
minor: Number(match[2]),
|
|
25
|
+
patch: Number(match[3])
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isSupportedPython(version) {
|
|
30
|
+
if (!version) return false;
|
|
31
|
+
if (version.major > MIN_PYTHON.major) return true;
|
|
32
|
+
return version.major === MIN_PYTHON.major && version.minor >= MIN_PYTHON.minor;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function findPython() {
|
|
36
|
+
for (const candidate of PYTHON_CANDIDATES) {
|
|
37
|
+
const args = candidate === "py" ? ["-3", "--version"] : ["--version"];
|
|
38
|
+
const result = run(candidate, args);
|
|
39
|
+
const version = parseVersion(`${result.stdout || ""}${result.stderr || ""}`);
|
|
40
|
+
if (result.status === 0 && isSupportedPython(version)) {
|
|
41
|
+
return {
|
|
42
|
+
command: candidate,
|
|
43
|
+
prefixArgs: candidate === "py" ? ["-3"] : [],
|
|
44
|
+
version
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function pythonArgs(python, args) {
|
|
52
|
+
return [...python.prefixArgs, ...args];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function hasValicodeModule(python) {
|
|
56
|
+
const result = run(python.command, pythonArgs(python, ["-c", "import aov.cli"]));
|
|
57
|
+
return result.status === 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function getCacheDir() {
|
|
61
|
+
if (process.env.VALICODE_NPM_CACHE_DIR) return process.env.VALICODE_NPM_CACHE_DIR;
|
|
62
|
+
const base = process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
|
|
63
|
+
return join(base, "valicode", "npm-cli");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getVenvPythonPath(venvDir) {
|
|
67
|
+
return process.platform === "win32" ? join(venvDir, "Scripts", "python.exe") : join(venvDir, "bin", "python");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function createVenv(systemPython, venvDir) {
|
|
71
|
+
mkdirSync(venvDir, { recursive: true });
|
|
72
|
+
return run(systemPython.command, pythonArgs(systemPython, ["-m", "venv", venvDir]), { stdio: "inherit" });
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function venvPython(venvDir) {
|
|
76
|
+
return {
|
|
77
|
+
command: getVenvPythonPath(venvDir),
|
|
78
|
+
prefixArgs: []
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function ensureVenv(systemPython) {
|
|
83
|
+
const venvDir = join(getCacheDir(), "python");
|
|
84
|
+
const pythonPath = getVenvPythonPath(venvDir);
|
|
85
|
+
if (!existsSync(pythonPath)) {
|
|
86
|
+
process.stderr.write(`Creating Valicode Python environment at ${venvDir}\n`);
|
|
87
|
+
const created = createVenv(systemPython, venvDir);
|
|
88
|
+
if (created.status !== 0 || !existsSync(pythonPath)) {
|
|
89
|
+
process.stderr.write(
|
|
90
|
+
[
|
|
91
|
+
"",
|
|
92
|
+
"Could not create a Python virtual environment for Valicode.",
|
|
93
|
+
"Install Python with venv support, then try again.",
|
|
94
|
+
"On Ubuntu/Debian: sudo apt install python3-venv",
|
|
95
|
+
"On macOS: install Python from python.org or Homebrew.",
|
|
96
|
+
""
|
|
97
|
+
].join("\n")
|
|
98
|
+
);
|
|
99
|
+
process.exit(created.status || 1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return venvPython(venvDir);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function installValicode(python) {
|
|
106
|
+
process.stderr.write("Installing/updating Valicode CLI from PyPI...\n");
|
|
107
|
+
return run(
|
|
108
|
+
python.command,
|
|
109
|
+
pythonArgs(python, ["-m", "pip", "install", "--upgrade", "valicode"]),
|
|
110
|
+
{ stdio: "inherit" }
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function printPythonHelp() {
|
|
115
|
+
process.stderr.write(
|
|
116
|
+
[
|
|
117
|
+
"Valicode requires Python 3.10 or newer.",
|
|
118
|
+
"",
|
|
119
|
+
"Install Python with venv support, then run one of:",
|
|
120
|
+
" npm install -g valicode",
|
|
121
|
+
" pipx install valicode",
|
|
122
|
+
" python3 -m pip install valicode",
|
|
123
|
+
""
|
|
124
|
+
].join("\n")
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function main() {
|
|
129
|
+
const python = findPython();
|
|
130
|
+
if (!python) {
|
|
131
|
+
printPythonHelp();
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const runtimePython = ensureVenv(python);
|
|
136
|
+
|
|
137
|
+
if (!hasValicodeModule(runtimePython)) {
|
|
138
|
+
const install = installValicode(runtimePython);
|
|
139
|
+
if (install.status !== 0 || !hasValicodeModule(runtimePython)) {
|
|
140
|
+
process.stderr.write(
|
|
141
|
+
[
|
|
142
|
+
"",
|
|
143
|
+
"Could not install the Valicode Python CLI automatically.",
|
|
144
|
+
"Try: pipx install valicode",
|
|
145
|
+
"Or install directly with pip inside your own virtual environment.",
|
|
146
|
+
""
|
|
147
|
+
].join("\n")
|
|
148
|
+
);
|
|
149
|
+
process.exit(install.status || 1);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const result = spawnSync(
|
|
154
|
+
runtimePython.command,
|
|
155
|
+
pythonArgs(runtimePython, [
|
|
156
|
+
"-c",
|
|
157
|
+
"import sys; sys.argv[0] = 'valicode'; from aov.cli import cli; cli()",
|
|
158
|
+
...process.argv.slice(2)
|
|
159
|
+
]),
|
|
160
|
+
{ stdio: "inherit", env: process.env }
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
if (result.error) {
|
|
164
|
+
process.stderr.write(`${result.error.message}\n`);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
process.exit(result.status ?? 0);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "valicode",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Valicode CLI npm wrapper",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"homepage": "https://valicode.sbs",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/in1v/valicode.git",
|
|
10
|
+
"directory": "packages/npm-cli"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/in1v/valicode-cli/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"ai",
|
|
17
|
+
"code-review",
|
|
18
|
+
"security",
|
|
19
|
+
"cli",
|
|
20
|
+
"github",
|
|
21
|
+
"valicode"
|
|
22
|
+
],
|
|
23
|
+
"bin": {
|
|
24
|
+
"valicode": "bin/valicode.js"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=18"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"test": "node test/smoke.js"
|
|
38
|
+
}
|
|
39
|
+
}
|