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/README.md CHANGED
@@ -1,179 +1,74 @@
1
- # Node.js CLI for Plagiarism Checker API
2
-
3
- ## Detail
4
- Command Line Interface (CLI) to interact with the Plagiarism Checker API
5
-
6
- ## Install
7
- ```
8
- npm install
9
- npm install -g .
10
- ```
11
-
12
- ## Usage
13
- After installation, you can use the following commands:
14
-
15
- 1. `auth`: Authenticate your API key
16
- 2. `createCheck`: Create a new plagiarism check
17
- 3. `uploadToCheck`: Upload a file to be checked
18
- 4. `startCheck`: Start a plagiarism check
19
- 5. `retriveCheck`: Retrieve existing checks
20
- 6. `checkStatus`: Check the status of a plagiarism check
21
- 7. `resultOverview`: Get an overview of the check results
22
- 8. `detailedResult`: Get detailed results of a check
23
-
24
- ## Examples
25
- Here are detailed examples of how to use each command:
26
-
27
- ### Authenticate
28
- ```
29
- codequiry auth
30
- ```
31
- This will prompt you to enter your API key for authentication.
32
- If you are not authenticated, you will be prompted to enter your API key:
33
- ```
34
- Enter your API key:
35
- ```
36
- Once you are authenticated successfully, it will save to apikey.json file and you can see following.
37
- ```
38
- Hello, lion!
39
- Your email is victor19930515@outlook.com.
40
- ? Choose an action:
41
- Create Check
42
- Start Check
43
- Upload to Check
44
- ```
45
- ### Create Check
46
- This will initiate the process of creating a new check and provide you with a check ID.
47
- ```
48
- codequiry createCheck
49
- ```
50
- You will be prompted to enter the programming language you want to check and name:
51
- ```
52
- ? Enter the programming language: 13
53
- ? Enter the your name:
54
- ```
55
- Available languages are:
56
- ```
57
- "available_languages": [
58
- { id: 13, language: 'Java (.java)' },
59
- { id: 14, language: 'Python (.py)' },
60
- { id: 16, language: 'C (.c/.h)' },
61
- { id: 17, language: 'C/C++ (.cc/.c/.h/.cpp/.hpp)' },
62
- { id: 18, language: 'C# (.cs)' },
63
- { id: 20, language: 'Perl (.pl/.sh)' },
64
- { id: 21, language: 'PHP (.php)' },
65
- { id: 22, language: 'SQL (.sql)' },
66
- { id: 23, language: 'VB (.vb/.bas)' },
67
- { id: 24, language: 'XML (.xml)' },
68
- { id: 28, language: 'Haskell (.hs/.lhs)' },
69
- { id: 29, language: 'Pascal (.pas/.inc)' },
70
- { id: 30, language: 'Go (.go)' },
71
- { id: 31, language: 'Matlab (.m)' },
72
- { id: 32, language: 'Lisp (.el)' },
73
- { id: 33, language: 'Ruby (.rb)' },
74
- { id: 34, language: 'Assembly (.asm/.s)' },
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)
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ 'use strict';
4
+
5
+ require('../src/index.js');
package/package.json CHANGED
@@ -1,26 +1,39 @@
1
- {
2
- "name": "codequiry-cli",
3
- "version": "1.0.0",
4
- "description": "Command Line Interface (CLI) to interact with the Code Plagiarism Checker API",
5
- "main": "index.js",
6
- "bin": {
7
- "codequiry": "src/index.js"
8
- },
9
- "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1"
11
- },
12
- "keywords": [],
13
- "author": "",
14
- "license": "ISC",
15
- "dependencies": {
16
- "axios": "^1.7.2",
17
- "chalk": "^5.3.0",
18
- "commander": "^12.1.0",
19
- "dotenv": "^16.4.5",
20
- "form-data": "^4.0.0",
21
- "fs": "^0.0.1-security",
22
- "fs-extra": "^11.2.0",
23
- "inquirer": "^10.1.2"
24
- },
25
- "type": "module"
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;