codequiry 2.0.1 → 2.1.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 +158 -123
- package/index.d.ts +123 -0
- package/index.js +227 -5
- package/package.json +50 -36
package/README.md
CHANGED
|
@@ -1,123 +1,158 @@
|
|
|
1
|
-
# Codequiry
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
|
86
|
-
|
|
87
|
-
| `cq.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
|
94
|
-
|
|
95
|
-
| `cq.
|
|
96
|
-
| `cq.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
1
|
+
# Codequiry Node.js SDK — Code Plagiarism Checker API
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/codequiry)
|
|
4
|
+
[](https://github.com/cqchecker/codequiry-sdk/blob/main/LICENSE)
|
|
5
|
+
[](https://www.npmjs.com/package/codequiry)
|
|
6
|
+
|
|
7
|
+
Official Node.js SDK for [Codequiry](https://codequiry.com), the code plagiarism checker. Run source code plagiarism detection, peer-to-peer code similarity analysis, and AI-generated code detection from Node.js, with TypeScript definitions included.
|
|
8
|
+
|
|
9
|
+
Codequiry checks submissions against billions of web sources, public repositories, and each other, across 65+ programming languages. It is used by universities, coding bootcamps, hiring teams, and competition organizers to verify code originality.
|
|
10
|
+
|
|
11
|
+
- **Web plagiarism check**: match code against online sources and repositories
|
|
12
|
+
- **Peer similarity check**: compare submissions against each other (group similarity)
|
|
13
|
+
- **AI code detection**: flag AI-generated code (ChatGPT, Copilot, and other LLMs)
|
|
14
|
+
- **Base code detection**: exclude shared starter/boilerplate code from scoring
|
|
15
|
+
|
|
16
|
+
More integrations: [Codequiry CLI](https://www.npmjs.com/package/codequiry-cli) · [Codequiry MCP server for AI agents](https://codequiry.com/mcp) · [REST API docs](https://codequiry.com/usage/api)
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install codequiry
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Requires Node.js 14+. Get an API key from your [Codequiry dashboard](https://codequiry.com/dashboard).
|
|
25
|
+
|
|
26
|
+
## Quick start
|
|
27
|
+
|
|
28
|
+
The fastest way to check code for plagiarism is `checkFiles`, which creates a check, uploads your ZIPs, starts the analysis, waits for completion, and returns the results:
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
const Codequiry = require('codequiry');
|
|
32
|
+
|
|
33
|
+
const cq = new Codequiry(process.env.CODEQUIRY_API_KEY);
|
|
34
|
+
|
|
35
|
+
const { overview, aiResults } = await cq.checkFiles('Assignment 1', ['./submissions.zip'], {
|
|
36
|
+
language: 999, // Auto Detect (or an id from getLanguages())
|
|
37
|
+
webcheck: true, // check against web sources
|
|
38
|
+
dbcheck: true, // check against Codequiry's database
|
|
39
|
+
aiRun: true, // also run AI-generated code detection
|
|
40
|
+
onProgress: (s) => console.log('status:', s.status),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
console.log(overview);
|
|
44
|
+
console.log(aiResults);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
TypeScript works out of the box:
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
import Codequiry = require('codequiry');
|
|
51
|
+
const cq = new Codequiry(process.env.CODEQUIRY_API_KEY!);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Step-by-step workflow
|
|
55
|
+
|
|
56
|
+
If you want control over each stage:
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
// 1. Create a check (999 = Auto Detect language)
|
|
60
|
+
const created = await cq.createCheck('Assignment 1', 999, { aiRun: true });
|
|
61
|
+
const checkId = created.check.id;
|
|
62
|
+
|
|
63
|
+
// 2. Upload one or more ZIP archives (max 10 MB each)
|
|
64
|
+
await cq.uploadFile(checkId, './submissions.zip');
|
|
65
|
+
|
|
66
|
+
// 3. Start the analysis
|
|
67
|
+
await cq.startCheck(checkId, { webcheck: true, dbcheck: true });
|
|
68
|
+
|
|
69
|
+
// 4. Wait for completion
|
|
70
|
+
await cq.pollUntilComplete(checkId, { onProgress: (s) => console.log(s.status) });
|
|
71
|
+
|
|
72
|
+
// 5. Read results
|
|
73
|
+
const overview = await cq.getOverview(checkId); // per-submission scores
|
|
74
|
+
const details = await cq.getResults(checkId, overview.submissions[0].id);
|
|
75
|
+
const ai = await cq.getAIResults(checkId); // AI detection results
|
|
76
|
+
const csv = await cq.getOverviewCSV(checkId); // overview as CSV
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API reference
|
|
80
|
+
|
|
81
|
+
### Account
|
|
82
|
+
|
|
83
|
+
| Method | Description |
|
|
84
|
+
|--------|-------------|
|
|
85
|
+
| `cq.validateKey()` | Validate the API key |
|
|
86
|
+
| `cq.account()` | Account info and usage quota |
|
|
87
|
+
| `cq.serviceStatus()` | Codequiry service status |
|
|
88
|
+
|
|
89
|
+
### Checks
|
|
90
|
+
|
|
91
|
+
| Method | Description |
|
|
92
|
+
|--------|-------------|
|
|
93
|
+
| `cq.checks()` | List all checks |
|
|
94
|
+
| `cq.createCheck(name, language?, options?)` | Create a check. Options: `{ testType, aiRun, baseCodeDetection, callbackUrl }` |
|
|
95
|
+
| `cq.getCheck(checkId)` | Check info and status |
|
|
96
|
+
| `cq.updateCheck(checkId, { name?, language? })` | Rename or change language |
|
|
97
|
+
| `cq.deleteCheck(checkId)` | Delete a check |
|
|
98
|
+
| `cq.deleteSubmission(checkId, submissionId)` | Delete one submission |
|
|
99
|
+
|
|
100
|
+
### Upload & start
|
|
101
|
+
|
|
102
|
+
| Method | Description |
|
|
103
|
+
|--------|-------------|
|
|
104
|
+
| `cq.uploadFile(checkId, filePath)` | Upload a ZIP (max 10 MB) |
|
|
105
|
+
| `cq.uploadBatch(checkId, filePaths)` | Upload multiple ZIPs |
|
|
106
|
+
| `cq.quickCheck(name, filePaths, options?)` | Create + upload + start in one call |
|
|
107
|
+
| `cq.startCheck(checkId, options?)` | Start. Options: `{ webcheck, dbcheck, testType, aiRun, baseCodeDetection }` |
|
|
108
|
+
| `cq.getStatus(checkId)` | Current status (for polling) |
|
|
109
|
+
|
|
110
|
+
### Results
|
|
111
|
+
|
|
112
|
+
| Method | Description |
|
|
113
|
+
|--------|-------------|
|
|
114
|
+
| `cq.getOverview(checkId)` | Results overview (per-submission similarity scores) |
|
|
115
|
+
| `cq.getOverviewCSV(checkId)` | Overview as CSV text |
|
|
116
|
+
| `cq.getResults(checkId, submissionId)` | Detailed matches for one submission |
|
|
117
|
+
| `cq.getSummary(checkId)` | Summary stats |
|
|
118
|
+
| `cq.getAIResults(checkId)` | AI-generated code detection results |
|
|
119
|
+
| `cq.exportResults(checkId, format?)` | Export results |
|
|
120
|
+
| `cq.getRemoteFile(location, source)` | Content of a matched web file |
|
|
121
|
+
| `cq.getViewer(checkId, submissionId?)` | Results-viewer payload |
|
|
122
|
+
| `cq.getViewerFile(checkId, submissionId, filePath)` | One submission file for the viewer |
|
|
123
|
+
| `cq.getViewerCounterpart(checkId, submissionId, matchId, legacy?)` | The other side of a match |
|
|
124
|
+
|
|
125
|
+
### Reference data & helpers
|
|
126
|
+
|
|
127
|
+
| Method | Description |
|
|
128
|
+
|--------|-------------|
|
|
129
|
+
| `cq.getLanguages()` | Supported languages (999 = Auto Detect) |
|
|
130
|
+
| `cq.getTestTypes()` | Available engines (test types) |
|
|
131
|
+
| `cq.pollUntilComplete(checkId, options?)` | Poll until done. Options: `{ interval, timeout, onProgress }` |
|
|
132
|
+
| `cq.checkFiles(name, filePaths, options?)` | Full workflow in one call |
|
|
133
|
+
|
|
134
|
+
## Webhooks
|
|
135
|
+
|
|
136
|
+
Pass `callbackUrl` when creating a check and Codequiry POSTs a signed JSON payload to your URL when the analysis completes, so you can skip polling:
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
await cq.createCheck('Assignment 1', 999, {
|
|
140
|
+
callbackUrl: 'https://your-app.example/codequiry-webhook',
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Errors and rate limits
|
|
145
|
+
|
|
146
|
+
All methods reject with an axios error on non-2xx responses; `err.response.data` carries the API's message. The API rate limits at 60 requests per minute per IP. Uploads must be ZIP archives of at most 10 MB; strip build output and dependencies before zipping.
|
|
147
|
+
|
|
148
|
+
## Links
|
|
149
|
+
|
|
150
|
+
- [Codequiry: code plagiarism checker](https://codequiry.com)
|
|
151
|
+
- [API documentation](https://codequiry.com/usage/api)
|
|
152
|
+
- [Codequiry CLI](https://www.npmjs.com/package/codequiry-cli) — run checks from the terminal
|
|
153
|
+
- [MCP server](https://codequiry.com/mcp) — plagiarism checks from Claude, Cursor, and other AI agents
|
|
154
|
+
- [AI code detection](https://codequiry.com/detect-ai-written-code)
|
|
155
|
+
|
|
156
|
+
## License
|
|
157
|
+
|
|
158
|
+
MIT © [Codequiry](https://codequiry.com)
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for the Codequiry Node.js SDK.
|
|
3
|
+
* Codequiry: code plagiarism checker, source code similarity detection,
|
|
4
|
+
* and AI-generated-code detection. https://codequiry.com
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface CreateCheckOptions {
|
|
8
|
+
/** Engine id from getTestTypes(). */
|
|
9
|
+
testType?: number;
|
|
10
|
+
/** Run AI-generated-code detection on this check. */
|
|
11
|
+
aiRun?: boolean;
|
|
12
|
+
/** Detect shared starter/boilerplate code. */
|
|
13
|
+
baseCodeDetection?: boolean;
|
|
14
|
+
/** Webhook URL called when the check completes. */
|
|
15
|
+
callbackUrl?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface StartCheckOptions {
|
|
19
|
+
/** Enable Codequiry database matching. */
|
|
20
|
+
dbcheck?: boolean;
|
|
21
|
+
/** Enable internet/web matching. */
|
|
22
|
+
webcheck?: boolean;
|
|
23
|
+
/** Engine id override. */
|
|
24
|
+
testType?: number;
|
|
25
|
+
/** Run AI-generated-code detection on this check. */
|
|
26
|
+
aiRun?: boolean;
|
|
27
|
+
/** Detect shared starter/boilerplate code. */
|
|
28
|
+
baseCodeDetection?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface QuickCheckOptions extends StartCheckOptions {
|
|
32
|
+
/** Language id from getLanguages(); 999 = Auto Detect. */
|
|
33
|
+
language?: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface PollOptions {
|
|
37
|
+
/** Poll interval in ms (default 3000). */
|
|
38
|
+
interval?: number;
|
|
39
|
+
/** Max wait in ms (default 600000). */
|
|
40
|
+
timeout?: number;
|
|
41
|
+
/** Called with each status payload while polling. */
|
|
42
|
+
onProgress?: (status: any) => void;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface CheckFilesOptions extends QuickCheckOptions, PollOptions {}
|
|
46
|
+
|
|
47
|
+
export interface CheckFilesResult {
|
|
48
|
+
check: any;
|
|
49
|
+
status: any;
|
|
50
|
+
overview: any;
|
|
51
|
+
aiResults?: any;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
declare class Codequiry {
|
|
55
|
+
/** @param apiKey Your Codequiry API key (https://codequiry.com/dashboard). */
|
|
56
|
+
constructor(apiKey: string);
|
|
57
|
+
|
|
58
|
+
/** Validate the API key. */
|
|
59
|
+
validateKey(): Promise<any>;
|
|
60
|
+
/** Get account info and usage quota. */
|
|
61
|
+
account(): Promise<any>;
|
|
62
|
+
|
|
63
|
+
/** List all checks. */
|
|
64
|
+
checks(): Promise<any>;
|
|
65
|
+
/** Create a new plagiarism check. */
|
|
66
|
+
createCheck(name: string, language?: number, options?: number | CreateCheckOptions): Promise<any>;
|
|
67
|
+
/** Get check info and status. */
|
|
68
|
+
getCheck(checkId: number): Promise<any>;
|
|
69
|
+
/** Update a check's name or language. */
|
|
70
|
+
updateCheck(checkId: number, fields: { name?: string; language?: number }): Promise<any>;
|
|
71
|
+
/** Delete a check. */
|
|
72
|
+
deleteCheck(checkId: number): Promise<any>;
|
|
73
|
+
/** Delete a single submission from a check. */
|
|
74
|
+
deleteSubmission(checkId: number, submissionId: number): Promise<any>;
|
|
75
|
+
|
|
76
|
+
/** Upload a ZIP file to a check (max 10 MB). */
|
|
77
|
+
uploadFile(checkId: number, filePath: string): Promise<any>;
|
|
78
|
+
/** Upload multiple ZIP files to a check. */
|
|
79
|
+
uploadBatch(checkId: number, filePaths: string[]): Promise<any>;
|
|
80
|
+
/** Create + upload + start in one call. */
|
|
81
|
+
quickCheck(name: string, filePaths: string | string[], options?: QuickCheckOptions): Promise<any>;
|
|
82
|
+
|
|
83
|
+
/** Start a check. */
|
|
84
|
+
startCheck(checkId: number, options?: StartCheckOptions): Promise<any>;
|
|
85
|
+
/** Get current check status. */
|
|
86
|
+
getStatus(checkId: number): Promise<any>;
|
|
87
|
+
|
|
88
|
+
/** Get results overview for a check. */
|
|
89
|
+
getOverview(checkId: number): Promise<any>;
|
|
90
|
+
/** Get the results overview as CSV text. */
|
|
91
|
+
getOverviewCSV(checkId: number): Promise<string>;
|
|
92
|
+
/** Get detailed results for one submission. */
|
|
93
|
+
getResults(checkId: number, submissionId: number): Promise<any>;
|
|
94
|
+
/** Get summary stats for a check. */
|
|
95
|
+
getSummary(checkId: number): Promise<any>;
|
|
96
|
+
/** Get AI-generated-code detection results. */
|
|
97
|
+
getAIResults(checkId: number): Promise<any>;
|
|
98
|
+
/** Export a check's results. */
|
|
99
|
+
exportResults(checkId: number, format?: string): Promise<any>;
|
|
100
|
+
/** Fetch the content of a matched web/remote file. */
|
|
101
|
+
getRemoteFile(location: string, source: string): Promise<any>;
|
|
102
|
+
|
|
103
|
+
/** Get the results-viewer payload for a check. */
|
|
104
|
+
getViewer(checkId: number, submissionId?: number): Promise<any>;
|
|
105
|
+
/** Get one submission file for the results viewer. */
|
|
106
|
+
getViewerFile(checkId: number, submissionId: number, filePath: string): Promise<any>;
|
|
107
|
+
/** Get the other side of a match in the results viewer. */
|
|
108
|
+
getViewerCounterpart(checkId: number, submissionId: number, matchId: number, legacy?: boolean): Promise<any>;
|
|
109
|
+
|
|
110
|
+
/** Get supported programming languages. */
|
|
111
|
+
getLanguages(): Promise<any>;
|
|
112
|
+
/** Get available engines (test types). */
|
|
113
|
+
getTestTypes(): Promise<any>;
|
|
114
|
+
/** Get Codequiry service status. */
|
|
115
|
+
serviceStatus(): Promise<any>;
|
|
116
|
+
|
|
117
|
+
/** Poll a check until completion. */
|
|
118
|
+
pollUntilComplete(checkId: number, options?: PollOptions): Promise<any>;
|
|
119
|
+
/** Quick-check ZIPs, wait for completion, return overview (and AI results). */
|
|
120
|
+
checkFiles(name: string, filePaths: string | string[], options?: CheckFilesOptions): Promise<CheckFilesResult>;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export = Codequiry;
|
package/index.js
CHANGED
|
@@ -48,16 +48,34 @@ class Codequiry {
|
|
|
48
48
|
return res.data;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Validate the API key.
|
|
53
|
+
* @returns {Promise<Object>}
|
|
54
|
+
*/
|
|
55
|
+
async validateKey() {
|
|
56
|
+
const res = await this.client.get('/auth/validate');
|
|
57
|
+
return res.data;
|
|
58
|
+
}
|
|
59
|
+
|
|
51
60
|
/**
|
|
52
61
|
* Create a new check.
|
|
53
62
|
* @param {string} name - Check name
|
|
54
|
-
* @param {number} language - Language ID (get from getLanguages())
|
|
55
|
-
* @param {number} [
|
|
63
|
+
* @param {number} [language] - Language ID (get from getLanguages(), 999 = Auto Detect)
|
|
64
|
+
* @param {number|Object} [options] - Test type ID (legacy), or options object
|
|
65
|
+
* @param {number} [options.testType] - Engine ID (get from getTestTypes())
|
|
66
|
+
* @param {boolean} [options.aiRun] - Run AI-generated-code detection
|
|
67
|
+
* @param {boolean} [options.baseCodeDetection] - Detect shared starter/boilerplate code
|
|
68
|
+
* @param {string} [options.callbackUrl] - Webhook URL called when the check completes
|
|
56
69
|
* @returns {Promise<Object>}
|
|
57
70
|
*/
|
|
58
|
-
async createCheck(name, language,
|
|
59
|
-
const
|
|
60
|
-
|
|
71
|
+
async createCheck(name, language, options) {
|
|
72
|
+
const opts = typeof options === 'number' ? { testType: options } : options || {};
|
|
73
|
+
const body = { name };
|
|
74
|
+
if (language) body.language = language;
|
|
75
|
+
if (opts.testType) body.test_type = opts.testType;
|
|
76
|
+
if (opts.aiRun !== undefined) body.ai_run = opts.aiRun ? 1 : 0;
|
|
77
|
+
if (opts.baseCodeDetection !== undefined) body.base_code_detection = opts.baseCodeDetection ? 1 : 0;
|
|
78
|
+
if (opts.callbackUrl) body.callback_url = opts.callbackUrl;
|
|
61
79
|
const res = await this.client.post('/check/create', body);
|
|
62
80
|
return res.data;
|
|
63
81
|
}
|
|
@@ -72,6 +90,22 @@ class Codequiry {
|
|
|
72
90
|
return res.data;
|
|
73
91
|
}
|
|
74
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Update a check's name or language.
|
|
95
|
+
* @param {number} checkId
|
|
96
|
+
* @param {Object} fields
|
|
97
|
+
* @param {string} [fields.name] - New check name
|
|
98
|
+
* @param {number} [fields.language] - New language ID (999 = Auto Detect)
|
|
99
|
+
* @returns {Promise<Object>}
|
|
100
|
+
*/
|
|
101
|
+
async updateCheck(checkId, fields = {}) {
|
|
102
|
+
const body = { check_id: checkId };
|
|
103
|
+
if (fields.name !== undefined) body.name = fields.name;
|
|
104
|
+
if (fields.language !== undefined) body.language = fields.language;
|
|
105
|
+
const res = await this.client.post('/check/update', body);
|
|
106
|
+
return res.data;
|
|
107
|
+
}
|
|
108
|
+
|
|
75
109
|
/**
|
|
76
110
|
* Delete a check.
|
|
77
111
|
* @param {number} checkId
|
|
@@ -82,6 +116,20 @@ class Codequiry {
|
|
|
82
116
|
return res.data;
|
|
83
117
|
}
|
|
84
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Delete a single submission from a check.
|
|
121
|
+
* @param {number} checkId
|
|
122
|
+
* @param {number} submissionId
|
|
123
|
+
* @returns {Promise<Object>}
|
|
124
|
+
*/
|
|
125
|
+
async deleteSubmission(checkId, submissionId) {
|
|
126
|
+
const res = await this.client.post('/check/deleteSubmission', {
|
|
127
|
+
check_id: checkId,
|
|
128
|
+
submission_id: submissionId,
|
|
129
|
+
});
|
|
130
|
+
return res.data;
|
|
131
|
+
}
|
|
132
|
+
|
|
85
133
|
// ─── Upload ───────────────────────────────────────────
|
|
86
134
|
|
|
87
135
|
/**
|
|
@@ -122,6 +170,40 @@ class Codequiry {
|
|
|
122
170
|
return res.data;
|
|
123
171
|
}
|
|
124
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Quick check: create a check, upload ZIPs, and start it in one call.
|
|
175
|
+
* @param {string} name - Check name
|
|
176
|
+
* @param {string[]} filePaths - Paths to one or more .zip files (max 10 MB each)
|
|
177
|
+
* @param {Object} [options]
|
|
178
|
+
* @param {number} [options.language] - Language ID (999 = Auto Detect)
|
|
179
|
+
* @param {boolean} [options.webcheck] - Enable internet/web matching
|
|
180
|
+
* @param {boolean} [options.dbcheck] - Enable Codequiry database matching
|
|
181
|
+
* @param {number} [options.testType] - Engine ID (overrides webcheck/dbcheck)
|
|
182
|
+
* @param {boolean} [options.aiRun] - Run AI-generated-code detection
|
|
183
|
+
* @param {boolean} [options.baseCodeDetection] - Detect shared starter/boilerplate code
|
|
184
|
+
* @returns {Promise<Object>}
|
|
185
|
+
*/
|
|
186
|
+
async quickCheck(name, filePaths, options = {}) {
|
|
187
|
+
const paths = Array.isArray(filePaths) ? filePaths : [filePaths];
|
|
188
|
+
const form = new FormData();
|
|
189
|
+
form.append('name', name);
|
|
190
|
+
if (options.language) form.append('language', String(options.language));
|
|
191
|
+
if (options.webcheck !== undefined) form.append('webcheck', options.webcheck ? '1' : '0');
|
|
192
|
+
if (options.dbcheck !== undefined) form.append('dbcheck', options.dbcheck ? '1' : '0');
|
|
193
|
+
if (options.testType) form.append('test_type', String(options.testType));
|
|
194
|
+
if (options.aiRun !== undefined) form.append('ai_run', options.aiRun ? '1' : '0');
|
|
195
|
+
if (options.baseCodeDetection !== undefined) {
|
|
196
|
+
form.append('base_code_detection', options.baseCodeDetection ? '1' : '0');
|
|
197
|
+
}
|
|
198
|
+
paths.forEach((fp) => form.append('files[]', fs.createReadStream(fp)));
|
|
199
|
+
const res = await this.client.post('/check/quick', form, {
|
|
200
|
+
headers: form.getHeaders(),
|
|
201
|
+
maxContentLength: Infinity,
|
|
202
|
+
maxBodyLength: Infinity,
|
|
203
|
+
});
|
|
204
|
+
return res.data;
|
|
205
|
+
}
|
|
206
|
+
|
|
125
207
|
// ─── Start & Status ───────────────────────────────────
|
|
126
208
|
|
|
127
209
|
/**
|
|
@@ -131,6 +213,8 @@ class Codequiry {
|
|
|
131
213
|
* @param {boolean} [options.dbcheck=false] - Enable database check
|
|
132
214
|
* @param {boolean} [options.webcheck=false] - Enable web check
|
|
133
215
|
* @param {number} [options.testType] - Test type override
|
|
216
|
+
* @param {boolean} [options.aiRun] - Run AI-generated-code detection
|
|
217
|
+
* @param {boolean} [options.baseCodeDetection] - Detect shared starter/boilerplate code
|
|
134
218
|
* @returns {Promise<Object>}
|
|
135
219
|
*/
|
|
136
220
|
async startCheck(checkId, options = {}) {
|
|
@@ -140,6 +224,10 @@ class Codequiry {
|
|
|
140
224
|
webcheck: options.webcheck ? 1 : 0,
|
|
141
225
|
};
|
|
142
226
|
if (options.testType) body.test_type = options.testType;
|
|
227
|
+
if (options.aiRun !== undefined) body.ai_run = options.aiRun ? 1 : 0;
|
|
228
|
+
if (options.baseCodeDetection !== undefined) {
|
|
229
|
+
body.base_code_detection = options.baseCodeDetection ? 1 : 0;
|
|
230
|
+
}
|
|
143
231
|
const res = await this.client.post('/check/start', body);
|
|
144
232
|
return res.data;
|
|
145
233
|
}
|
|
@@ -190,6 +278,113 @@ class Codequiry {
|
|
|
190
278
|
return res.data;
|
|
191
279
|
}
|
|
192
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Get the results overview as CSV.
|
|
283
|
+
* @param {number} checkId
|
|
284
|
+
* @returns {Promise<string>} CSV text
|
|
285
|
+
*/
|
|
286
|
+
async getOverviewCSV(checkId) {
|
|
287
|
+
const res = await this.client.post(
|
|
288
|
+
'/check/overviewCSV',
|
|
289
|
+
{ check_id: checkId },
|
|
290
|
+
{ responseType: 'text', transformResponse: [(d) => d] }
|
|
291
|
+
);
|
|
292
|
+
return res.data;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Get AI-generated-code detection results for a check.
|
|
297
|
+
* @param {number} checkId
|
|
298
|
+
* @returns {Promise<Object>}
|
|
299
|
+
*/
|
|
300
|
+
async getAIResults(checkId) {
|
|
301
|
+
const res = await this.client.get('/ai-results', {
|
|
302
|
+
params: { assignment_id: checkId },
|
|
303
|
+
});
|
|
304
|
+
return res.data;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Export a check's results.
|
|
309
|
+
* @param {number} checkId
|
|
310
|
+
* @param {string} [format] - Export format (e.g. 'json')
|
|
311
|
+
* @returns {Promise<Object>}
|
|
312
|
+
*/
|
|
313
|
+
async exportResults(checkId, format) {
|
|
314
|
+
const body = { check_id: checkId };
|
|
315
|
+
if (format) body.format = format;
|
|
316
|
+
const res = await this.client.post('/check/export', body);
|
|
317
|
+
return res.data;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Fetch the content of a matched web/remote file.
|
|
322
|
+
* @param {string} location - Match location from results
|
|
323
|
+
* @param {string} source - Match source from results
|
|
324
|
+
* @returns {Promise<Object>}
|
|
325
|
+
*/
|
|
326
|
+
async getRemoteFile(location, source) {
|
|
327
|
+
const res = await this.client.post('/check/getRemoteFile', { location, source });
|
|
328
|
+
return res.data;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ─── Results Viewer ───────────────────────────────────
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Get the results-viewer payload for a check.
|
|
335
|
+
* @param {number} checkId
|
|
336
|
+
* @param {number} [submissionId] - Omit for the highest-scoring submission
|
|
337
|
+
* @returns {Promise<Object>}
|
|
338
|
+
*/
|
|
339
|
+
async getViewer(checkId, submissionId) {
|
|
340
|
+
const body = { check_id: checkId };
|
|
341
|
+
if (submissionId) body.submission_id = submissionId;
|
|
342
|
+
const res = await this.client.post('/check/viewer', body);
|
|
343
|
+
return res.data;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Get one submission file for the results viewer.
|
|
348
|
+
* @param {number} checkId
|
|
349
|
+
* @param {number} submissionId
|
|
350
|
+
* @param {string} filePath - The file's `filedir` from the viewer payload
|
|
351
|
+
* @returns {Promise<Object>}
|
|
352
|
+
*/
|
|
353
|
+
async getViewerFile(checkId, submissionId, filePath) {
|
|
354
|
+
const res = await this.client.post('/check/viewer/file', {
|
|
355
|
+
check_id: checkId,
|
|
356
|
+
submission_id: submissionId,
|
|
357
|
+
file_path: filePath,
|
|
358
|
+
});
|
|
359
|
+
return res.data;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Get the other side of a match in the results viewer.
|
|
364
|
+
* @param {number} checkId
|
|
365
|
+
* @param {number} submissionId
|
|
366
|
+
* @param {number} matchId - The `id` of an entry in `matches` or `web_files`
|
|
367
|
+
* @param {boolean} [legacy] - true when the entry's `is_legacy_web` is true
|
|
368
|
+
* @returns {Promise<Object>}
|
|
369
|
+
*/
|
|
370
|
+
async getViewerCounterpart(checkId, submissionId, matchId, legacy) {
|
|
371
|
+
const body = { check_id: checkId, submission_id: submissionId, match_id: matchId };
|
|
372
|
+
if (legacy !== undefined) body.legacy = legacy;
|
|
373
|
+
const res = await this.client.post('/check/viewer/counterpart', body);
|
|
374
|
+
return res.data;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ─── Service ──────────────────────────────────────────
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Get Codequiry service status.
|
|
381
|
+
* @returns {Promise<Object>}
|
|
382
|
+
*/
|
|
383
|
+
async serviceStatus() {
|
|
384
|
+
const res = await this.client.get('/status');
|
|
385
|
+
return res.data;
|
|
386
|
+
}
|
|
387
|
+
|
|
193
388
|
// ─── Reference Data ───────────────────────────────────
|
|
194
389
|
|
|
195
390
|
/**
|
|
@@ -248,6 +443,33 @@ class Codequiry {
|
|
|
248
443
|
|
|
249
444
|
throw new Error('Polling timed out after ' + timeout + 'ms');
|
|
250
445
|
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* One-call workflow: quick-check the given ZIPs, wait for completion,
|
|
449
|
+
* and return the results overview (plus AI results when aiRun is set).
|
|
450
|
+
* @param {string} name - Check name
|
|
451
|
+
* @param {string|string[]} filePaths - Path(s) to .zip files
|
|
452
|
+
* @param {Object} [options] - quickCheck options plus poll interval/timeout/onProgress
|
|
453
|
+
* @returns {Promise<{check: Object, status: Object, overview: Object, aiResults?: Object}>}
|
|
454
|
+
*/
|
|
455
|
+
async checkFiles(name, filePaths, options = {}) {
|
|
456
|
+
const created = await this.quickCheck(name, filePaths, options);
|
|
457
|
+
const checkId = created?.check?.id || created?.id || created?.check_id;
|
|
458
|
+
if (!checkId) {
|
|
459
|
+
throw new Error('Quick check did not return a check id: ' + JSON.stringify(created));
|
|
460
|
+
}
|
|
461
|
+
const status = await this.pollUntilComplete(checkId, options);
|
|
462
|
+
const overview = await this.getOverview(checkId);
|
|
463
|
+
const result = { check: created, status, overview };
|
|
464
|
+
if (options.aiRun) {
|
|
465
|
+
try {
|
|
466
|
+
result.aiResults = await this.getAIResults(checkId);
|
|
467
|
+
} catch (err) {
|
|
468
|
+
result.aiResults = { error: err.message };
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return result;
|
|
472
|
+
}
|
|
251
473
|
}
|
|
252
474
|
|
|
253
475
|
module.exports = Codequiry;
|
package/package.json
CHANGED
|
@@ -1,36 +1,50 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "codequiry",
|
|
3
|
-
"version": "2.0
|
|
4
|
-
"description": "Node.js SDK for Codequiry
|
|
5
|
-
"main": "index.js",
|
|
6
|
-
"
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
36
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "codequiry",
|
|
3
|
+
"version": "2.1.0",
|
|
4
|
+
"description": "Official Node.js SDK for Codequiry, the code plagiarism checker: source code plagiarism detection, peer code similarity, and AI-generated code detection API",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"index.js",
|
|
9
|
+
"index.d.ts",
|
|
10
|
+
"README.md",
|
|
11
|
+
"LICENSE"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "node test/smoke.js"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/cqchecker/codequiry-sdk.git"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"codequiry",
|
|
22
|
+
"plagiarism",
|
|
23
|
+
"plagiarism-checker",
|
|
24
|
+
"code-plagiarism",
|
|
25
|
+
"code-plagiarism-checker",
|
|
26
|
+
"code-similarity",
|
|
27
|
+
"similarity-detection",
|
|
28
|
+
"source-code-plagiarism",
|
|
29
|
+
"ai-code-detection",
|
|
30
|
+
"ai-detection",
|
|
31
|
+
"academic-integrity",
|
|
32
|
+
"moss",
|
|
33
|
+
"moss-alternative",
|
|
34
|
+
"api",
|
|
35
|
+
"sdk"
|
|
36
|
+
],
|
|
37
|
+
"author": "Codequiry <contact@codequiry.com> (https://codequiry.com)",
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/cqchecker/codequiry-sdk/issues"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://codequiry.com/sdks",
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"axios": "^1.6.0",
|
|
45
|
+
"form-data": "^4.0.0"
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=14.0.0"
|
|
49
|
+
}
|
|
50
|
+
}
|