codequiry-cli 1.0.0 → 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/LICENSE +21 -674
- package/README.md +74 -179
- package/bin/codequiry.js +5 -0
- package/package.json +39 -26
- package/src/api/client.js +35 -0
- package/src/api/endpoints.js +148 -0
- package/src/commands/account.js +55 -0
- package/src/commands/auth.js +56 -0
- package/src/commands/checks.js +22 -0
- package/src/commands/create.js +85 -0
- package/src/commands/delete.js +72 -0
- package/src/commands/results.js +117 -0
- package/src/commands/scan.js +189 -0
- package/src/commands/start.js +76 -0
- package/src/commands/status.js +65 -0
- package/src/commands/upload.js +102 -0
- package/src/index.js +132 -51
- package/src/ui/menu.js +85 -0
- package/src/ui/prompts.js +86 -0
- package/src/utils/banner.js +20 -0
- package/src/utils/config.js +67 -0
- package/src/utils/display.js +116 -0
- package/src/utils/errors.js +63 -0
- package/src/utils/poller.js +60 -0
- package/src/utils/zipper.js +96 -0
- package/src/auth.js +0 -47
- package/src/check.js +0 -444
- package/src/const.js +0 -56
- package/src/util.js +0 -26
- package/uploads/.gitkeep +0 -0
package/README.md
CHANGED
|
@@ -1,179 +1,74 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
```
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
codequiry
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
codequiry
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
{ id: 38, language: 'HTML Javascript (.html/.htm/.xhtml)' },
|
|
76
|
-
{ id: 39, language: 'Javascript (.js/.ts)' },
|
|
77
|
-
{ id: 40, language: 'HTML (.html/.htm/.xhtml)' },
|
|
78
|
-
{ id: 41, language: 'Plain text (.txt)' },
|
|
79
|
-
{ id: 42, language: 'Text file by char (.txt)' },
|
|
80
|
-
{ id: 43, language: 'Swift (.swift)' },
|
|
81
|
-
{ id: 44, language: 'Kotlin (.kt/.kts)' },
|
|
82
|
-
{ id: 45, language: 'Yacc (.y,.yy,.ypp,.yxx)' },
|
|
83
|
-
{ id: 46, language: 'Lex (.l,.ll)' },
|
|
84
|
-
{ id: 47, language: 'Elixir (.ex, .exs)' },
|
|
85
|
-
{ id: 48, language: 'Python Jupyter Notebook (.ipynb)' },
|
|
86
|
-
{ id: 49, language: 'Dart (.dart)' },
|
|
87
|
-
{ id: 50, language: 'Shell (.sh/.bash)' },
|
|
88
|
-
{ id: 51, language: 'Rust (.rs)' },
|
|
89
|
-
{ id: 52, language: 'Scala (.scala)' },
|
|
90
|
-
{ id: 53, language: 'R (.r)' },
|
|
91
|
-
{ id: 54, language: 'Objective-C (.m, .mm)' },
|
|
92
|
-
{ id: 55, language: 'TypeScript (.ts, .tsx)' },
|
|
93
|
-
{ id: 56, language: 'Markdown (.md)' },
|
|
94
|
-
{ id: 57, language: 'Julia (.jl)' },
|
|
95
|
-
{ id: 58, language: 'Groovy (.groovy)' },
|
|
96
|
-
{ id: 59, language: 'Sass/SCSS (.scss, .sass)' },
|
|
97
|
-
{ id: 60, language: 'CoffeeScript (.coffee)' },
|
|
98
|
-
{ id: 61, language: 'Lua (.lua)' },
|
|
99
|
-
{ id: 62, language: 'Erlang (.erl, .hrl)' },
|
|
100
|
-
{ id: 63, language: 'F# (.fs, .fsi, .fsx)' },
|
|
101
|
-
{ id: 64, language: 'Fortran (.f90, .f95)' },
|
|
102
|
-
{ id: 65, language: 'Haxe (.hx)' },
|
|
103
|
-
{ id: 66, language: 'Scheme (.scm, .ss)' },
|
|
104
|
-
{ id: 67, language: 'Tcl (.tcl)' },
|
|
105
|
-
{ id: 68, language: 'Ada (.adb, .ads)' },
|
|
106
|
-
{ id: 69, language: 'COBOL (.cob, .cbl)' },
|
|
107
|
-
{ id: 70, language: 'VHDL (.vhd, .vhdl)' }
|
|
108
|
-
]
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
### Upload to Check
|
|
112
|
-
This will upload files in uploads folder to check.
|
|
113
|
-
**Files in uploads folder must be zip files.**
|
|
114
|
-
```
|
|
115
|
-
codequiry uploadToCheck
|
|
116
|
-
```
|
|
117
|
-
You will be prompted to enter check ID.
|
|
118
|
-
```
|
|
119
|
-
? Enter the check ID:
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### Start Check
|
|
123
|
-
This will start check.
|
|
124
|
-
```
|
|
125
|
-
codequiry startCheck
|
|
126
|
-
```
|
|
127
|
-
You will be prompted to enter check ID.
|
|
128
|
-
```
|
|
129
|
-
? Enter the check ID:
|
|
130
|
-
? Select the additional check type: (Use arrow keys)
|
|
131
|
-
❯ webcheck
|
|
132
|
-
dbcheck
|
|
133
|
-
```
|
|
134
|
-
```
|
|
135
|
-
dbcheck: If you would like to run the database check
|
|
136
|
-
webcheck: If you would like to run the web check
|
|
137
|
-
```
|
|
138
|
-
### Retrive Check
|
|
139
|
-
This will retrive check.
|
|
140
|
-
```
|
|
141
|
-
codequiry retriveCheck
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### Get Check Status
|
|
145
|
-
This will get check status.
|
|
146
|
-
```
|
|
147
|
-
codequiry checkStatus
|
|
148
|
-
```
|
|
149
|
-
You will be prompted to enter check ID.
|
|
150
|
-
```
|
|
151
|
-
? Enter the check ID:
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
### Get Result Overview
|
|
155
|
-
This will get result overview.
|
|
156
|
-
```
|
|
157
|
-
codequiry resultOverview
|
|
158
|
-
```
|
|
159
|
-
You will be prompted to enter check ID.
|
|
160
|
-
```
|
|
161
|
-
? Enter the check ID:
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Get Detailed Result
|
|
165
|
-
This will get detailed result.
|
|
166
|
-
```
|
|
167
|
-
codequiry detailedResult
|
|
168
|
-
```
|
|
169
|
-
You will be prompted to enter check ID and submission ID.
|
|
170
|
-
```
|
|
171
|
-
? Enter the check ID: 95663
|
|
172
|
-
? Enter the submission ID: 220801
|
|
173
|
-
```
|
|
174
|
-
|
|
175
|
-
## Version
|
|
176
|
-
Current version: 1.0.0
|
|
177
|
-
|
|
178
|
-
## License
|
|
179
|
-
ISC
|
|
1
|
+
# Codequiry CLI
|
|
2
|
+
|
|
3
|
+
Source code similarity checker from your terminal. Detect plagiarism across 65+ programming languages.
|
|
4
|
+
|
|
5
|
+
## Quick Install
|
|
6
|
+
|
|
7
|
+
**macOS / Linux:**
|
|
8
|
+
```bash
|
|
9
|
+
curl -fsSL https://codequiry.com/install.sh | bash
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Windows (PowerShell):**
|
|
13
|
+
```powershell
|
|
14
|
+
irm https://codequiry.com/install.ps1 | iex
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**NPM:**
|
|
18
|
+
```bash
|
|
19
|
+
npm install -g codequiry-cli
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Authenticate
|
|
26
|
+
codequiry auth
|
|
27
|
+
|
|
28
|
+
# Scan a folder (does everything in one command)
|
|
29
|
+
codequiry scan ./submissions
|
|
30
|
+
|
|
31
|
+
# Interactive mode
|
|
32
|
+
codequiry
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Commands
|
|
36
|
+
|
|
37
|
+
| Command | Description |
|
|
38
|
+
|---------|-------------|
|
|
39
|
+
| `codequiry` | Interactive TUI mode |
|
|
40
|
+
| `codequiry scan [path]` | Full scan: zip, upload, start, poll, results |
|
|
41
|
+
| `codequiry auth` | Set your API key |
|
|
42
|
+
| `codequiry checks` | List all checks |
|
|
43
|
+
| `codequiry create` | Create a new check |
|
|
44
|
+
| `codequiry upload [path]` | Upload files to a check |
|
|
45
|
+
| `codequiry start` | Start a check |
|
|
46
|
+
| `codequiry status` | Check progress |
|
|
47
|
+
| `codequiry results` | View results |
|
|
48
|
+
| `codequiry account` | Account info & quota |
|
|
49
|
+
| `codequiry delete` | Delete a check |
|
|
50
|
+
|
|
51
|
+
## CI/CD
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# JSON output for pipelines
|
|
55
|
+
codequiry scan ./src --output json
|
|
56
|
+
|
|
57
|
+
# Fail if similarity exceeds threshold
|
|
58
|
+
codequiry scan ./src --threshold 70
|
|
59
|
+
|
|
60
|
+
# Auth via environment variable
|
|
61
|
+
export CODEQUIRY_API_KEY=your-key-here
|
|
62
|
+
codequiry scan ./src --output json
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Requirements
|
|
66
|
+
|
|
67
|
+
- Node.js 18+
|
|
68
|
+
- API key from [codequiry.com](https://codequiry.com) (Grow or Advanced plan)
|
|
69
|
+
|
|
70
|
+
## Links
|
|
71
|
+
|
|
72
|
+
- [Codequiry](https://codequiry.com)
|
|
73
|
+
- [API Documentation](https://codequiry.com/usage/docs)
|
|
74
|
+
- [Get API Key](https://codequiry.com/dashboard)
|
package/bin/codequiry.js
ADDED
package/package.json
CHANGED
|
@@ -1,26 +1,39 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "codequiry-cli",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"codequiry": "
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
"
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"
|
|
23
|
-
"inquirer": "^
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "codequiry-cli",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "Codequiry CLI - Source code similarity checker",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"codequiry": "./bin/codequiry.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/codequiry.js",
|
|
11
|
+
"test": "echo \"No tests yet\""
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"codequiry",
|
|
15
|
+
"plagiarism",
|
|
16
|
+
"code-similarity",
|
|
17
|
+
"cli"
|
|
18
|
+
],
|
|
19
|
+
"author": "Codequiry",
|
|
20
|
+
"license": "MIT",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"commander": "^12.0.0",
|
|
23
|
+
"inquirer": "^8.2.6",
|
|
24
|
+
"chalk": "^4.1.2",
|
|
25
|
+
"ora": "^5.4.1",
|
|
26
|
+
"cli-table3": "^0.6.0",
|
|
27
|
+
"cli-progress": "^3.12.0",
|
|
28
|
+
"axios": "^1.6.0",
|
|
29
|
+
"form-data": "^4.0.0",
|
|
30
|
+
"archiver": "^6.0.0",
|
|
31
|
+
"open": "^8.4.2",
|
|
32
|
+
"figlet": "^1.7.0",
|
|
33
|
+
"boxen": "^5.1.2",
|
|
34
|
+
"gradient-string": "^2.0.0"
|
|
35
|
+
},
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const axios = require('axios');
|
|
4
|
+
const { getApiKey } = require('../utils/config');
|
|
5
|
+
const { handleApiError } = require('../utils/errors');
|
|
6
|
+
|
|
7
|
+
const BASE_URL = 'https://codequiry.com/api/v1';
|
|
8
|
+
|
|
9
|
+
function createClient() {
|
|
10
|
+
const apiKey = getApiKey();
|
|
11
|
+
if (!apiKey) {
|
|
12
|
+
throw new Error('Not authenticated. Run `codequiry auth` to set your API key.');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const client = axios.create({
|
|
16
|
+
baseURL: BASE_URL,
|
|
17
|
+
headers: {
|
|
18
|
+
apikey: apiKey,
|
|
19
|
+
Accept: 'application/json',
|
|
20
|
+
},
|
|
21
|
+
timeout: 120000,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
client.interceptors.response.use(
|
|
25
|
+
(response) => response,
|
|
26
|
+
(error) => {
|
|
27
|
+
handleApiError(error);
|
|
28
|
+
return Promise.reject(error);
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
return client;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = { createClient, BASE_URL };
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const FormData = require('form-data');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { createClient } = require('./client');
|
|
6
|
+
|
|
7
|
+
async function validateKey(apiKey) {
|
|
8
|
+
const axios = require('axios');
|
|
9
|
+
const res = await axios.post(
|
|
10
|
+
'https://codequiry.com/api/v1/auth/validate',
|
|
11
|
+
{},
|
|
12
|
+
{ headers: { apikey: apiKey, Accept: 'application/json' } }
|
|
13
|
+
);
|
|
14
|
+
return res.data;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function getAccount() {
|
|
18
|
+
const client = createClient();
|
|
19
|
+
const res = await client.get('/account');
|
|
20
|
+
return res.data;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function getLanguages() {
|
|
24
|
+
const client = createClient();
|
|
25
|
+
const res = await client.get('/languages');
|
|
26
|
+
return res.data;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function getTestTypes() {
|
|
30
|
+
const client = createClient();
|
|
31
|
+
const res = await client.get('/test-types');
|
|
32
|
+
return res.data;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function listChecks() {
|
|
36
|
+
const client = createClient();
|
|
37
|
+
const res = await client.get('/checks');
|
|
38
|
+
return res.data;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function createCheck(name, languageId, testType) {
|
|
42
|
+
const client = createClient();
|
|
43
|
+
const res = await client.post('/check/create', {
|
|
44
|
+
name,
|
|
45
|
+
language: languageId,
|
|
46
|
+
test_type: testType,
|
|
47
|
+
});
|
|
48
|
+
return res.data;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async function uploadZip(checkId, filePath) {
|
|
52
|
+
const client = createClient();
|
|
53
|
+
const form = new FormData();
|
|
54
|
+
form.append('check_id', String(checkId));
|
|
55
|
+
form.append('file', fs.createReadStream(filePath));
|
|
56
|
+
|
|
57
|
+
const res = await client.post('/check/upload', form, {
|
|
58
|
+
headers: {
|
|
59
|
+
...form.getHeaders(),
|
|
60
|
+
},
|
|
61
|
+
maxContentLength: Infinity,
|
|
62
|
+
maxBodyLength: Infinity,
|
|
63
|
+
});
|
|
64
|
+
return res.data;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function uploadBatch(checkId, filePaths) {
|
|
68
|
+
const client = createClient();
|
|
69
|
+
const form = new FormData();
|
|
70
|
+
form.append('check_id', String(checkId));
|
|
71
|
+
filePaths.forEach((fp) => {
|
|
72
|
+
form.append('files[]', fs.createReadStream(fp));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const res = await client.post('/check/upload-batch', form, {
|
|
76
|
+
headers: {
|
|
77
|
+
...form.getHeaders(),
|
|
78
|
+
},
|
|
79
|
+
maxContentLength: Infinity,
|
|
80
|
+
maxBodyLength: Infinity,
|
|
81
|
+
});
|
|
82
|
+
return res.data;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function startCheck(checkId, webcheck = 0, dbcheck = 0, testType) {
|
|
86
|
+
const client = createClient();
|
|
87
|
+
const body = { check_id: checkId, webcheck, dbcheck };
|
|
88
|
+
if (testType) body.test_type = testType;
|
|
89
|
+
const res = await client.post('/check/start', body);
|
|
90
|
+
return res.data;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function getCheckStatus(checkId) {
|
|
94
|
+
const client = createClient();
|
|
95
|
+
const res = await client.get(`/checks/${checkId}/status`);
|
|
96
|
+
return res.data;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async function getOverview(checkId) {
|
|
100
|
+
const client = createClient();
|
|
101
|
+
const res = await client.post('/check/overview', { check_id: checkId });
|
|
102
|
+
return res.data;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function getResults(checkId, submissionId) {
|
|
106
|
+
const client = createClient();
|
|
107
|
+
const res = await client.post('/check/results', {
|
|
108
|
+
check_id: checkId,
|
|
109
|
+
submission_id: submissionId,
|
|
110
|
+
});
|
|
111
|
+
return res.data;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async function getSummary(checkId) {
|
|
115
|
+
const client = createClient();
|
|
116
|
+
const res = await client.get(`/checks/${checkId}/summary`);
|
|
117
|
+
return res.data;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function deleteCheck(checkId) {
|
|
121
|
+
const client = createClient();
|
|
122
|
+
const res = await client.delete(`/checks/${checkId}`);
|
|
123
|
+
return res.data;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async function getAIResults(checkId) {
|
|
127
|
+
const client = createClient();
|
|
128
|
+
const res = await client.get(`/checks/${checkId}/ai-results`);
|
|
129
|
+
return res.data;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = {
|
|
133
|
+
validateKey,
|
|
134
|
+
getAccount,
|
|
135
|
+
getLanguages,
|
|
136
|
+
getTestTypes,
|
|
137
|
+
listChecks,
|
|
138
|
+
createCheck,
|
|
139
|
+
uploadZip,
|
|
140
|
+
uploadBatch,
|
|
141
|
+
startCheck,
|
|
142
|
+
getCheckStatus,
|
|
143
|
+
getOverview,
|
|
144
|
+
getResults,
|
|
145
|
+
getSummary,
|
|
146
|
+
deleteCheck,
|
|
147
|
+
getAIResults,
|
|
148
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const boxen = require('boxen');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
const { requireAuth } = require('../utils/errors');
|
|
7
|
+
const { getAccount } = require('../api/endpoints');
|
|
8
|
+
|
|
9
|
+
async function accountCommand() {
|
|
10
|
+
requireAuth();
|
|
11
|
+
|
|
12
|
+
const spinner = ora('Fetching account info...').start();
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
const account = await getAccount();
|
|
16
|
+
spinner.stop();
|
|
17
|
+
|
|
18
|
+
const user = account.user || account;
|
|
19
|
+
const plan = user.plan_name || user.plan || 'Free';
|
|
20
|
+
const checksUsed = user.checks_used ?? user.procheck_used ?? 0;
|
|
21
|
+
const checksLimit = user.checks_limit ?? user.procheck_limit ?? 0;
|
|
22
|
+
const quotaPct = checksLimit > 0 ? Math.round((checksUsed / checksLimit) * 100) : 0;
|
|
23
|
+
const barLen = 20;
|
|
24
|
+
const filled = Math.round((quotaPct / 100) * barLen);
|
|
25
|
+
const quotaBar = chalk.green('█'.repeat(filled)) + chalk.dim('░'.repeat(barLen - filled));
|
|
26
|
+
|
|
27
|
+
const content = [
|
|
28
|
+
chalk.bold('Account'),
|
|
29
|
+
'',
|
|
30
|
+
`${chalk.dim('Name:')} ${user.name || user.email || 'N/A'}`,
|
|
31
|
+
`${chalk.dim('Email:')} ${user.email || 'N/A'}`,
|
|
32
|
+
`${chalk.dim('Plan:')} ${chalk.cyan(plan)}`,
|
|
33
|
+
'',
|
|
34
|
+
chalk.bold('Usage'),
|
|
35
|
+
'',
|
|
36
|
+
`${chalk.dim('Checks:')} ${checksUsed} / ${checksLimit}`,
|
|
37
|
+
` ${quotaBar} ${quotaPct}%`,
|
|
38
|
+
].join('\n');
|
|
39
|
+
|
|
40
|
+
console.log(
|
|
41
|
+
'\n' +
|
|
42
|
+
boxen(content, {
|
|
43
|
+
padding: 1,
|
|
44
|
+
margin: 1,
|
|
45
|
+
borderStyle: 'round',
|
|
46
|
+
borderColor: 'cyan',
|
|
47
|
+
}) +
|
|
48
|
+
'\n'
|
|
49
|
+
);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
spinner.fail('Failed to fetch account info.');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
module.exports = accountCommand;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const ora = require('ora');
|
|
6
|
+
const { getApiKey, setApiKey, clearApiKey, isAuthenticated, CONFIG_FILE } = require('../utils/config');
|
|
7
|
+
const { validateKey } = require('../api/endpoints');
|
|
8
|
+
|
|
9
|
+
async function authCommand(options) {
|
|
10
|
+
if (options.logout) {
|
|
11
|
+
clearApiKey();
|
|
12
|
+
console.log(chalk.green('\n✔ Logged out successfully.\n'));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (options.status) {
|
|
17
|
+
if (isAuthenticated()) {
|
|
18
|
+
const key = getApiKey();
|
|
19
|
+
const masked = key.substring(0, 6) + '...' + key.substring(key.length - 4);
|
|
20
|
+
console.log(chalk.green('\n✔ Authenticated'));
|
|
21
|
+
console.log(chalk.dim(` Key: ${masked}`));
|
|
22
|
+
console.log(chalk.dim(` Config: ${CONFIG_FILE}\n`));
|
|
23
|
+
} else {
|
|
24
|
+
console.log(chalk.yellow('\n⚠ Not authenticated.'));
|
|
25
|
+
console.log(chalk.dim(' Run `codequiry auth` to set your API key.\n'));
|
|
26
|
+
}
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Interactive login
|
|
31
|
+
const { apiKey } = await inquirer.prompt([
|
|
32
|
+
{
|
|
33
|
+
type: 'password',
|
|
34
|
+
name: 'apiKey',
|
|
35
|
+
message: 'Enter your Codequiry API key:',
|
|
36
|
+
mask: '*',
|
|
37
|
+
validate: (input) => (input.length > 0 ? true : 'API key is required'),
|
|
38
|
+
},
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
const spinner = ora('Validating API key...').start();
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
await validateKey(apiKey);
|
|
45
|
+
setApiKey(apiKey);
|
|
46
|
+
spinner.succeed('API key validated and saved!');
|
|
47
|
+
console.log(chalk.dim(` Config: ${CONFIG_FILE}\n`));
|
|
48
|
+
} catch (error) {
|
|
49
|
+
spinner.fail('Invalid API key.');
|
|
50
|
+
if (error.response?.status === 401) {
|
|
51
|
+
console.error(chalk.dim(' Please check your key at https://codequiry.com/dashboard\n'));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = authCommand;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ora = require('ora');
|
|
4
|
+
const { requireAuth } = require('../utils/errors');
|
|
5
|
+
const { listChecks } = require('../api/endpoints');
|
|
6
|
+
const { checksTable } = require('../utils/display');
|
|
7
|
+
|
|
8
|
+
async function checksCommand() {
|
|
9
|
+
requireAuth();
|
|
10
|
+
|
|
11
|
+
const spinner = ora('Fetching checks...').start();
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const data = await listChecks();
|
|
15
|
+
spinner.stop();
|
|
16
|
+
checksTable(data);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
spinner.fail('Failed to fetch checks.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = checksCommand;
|