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.
Files changed (4) hide show
  1. package/README.md +158 -123
  2. package/index.d.ts +123 -0
  3. package/index.js +227 -5
  4. package/package.json +50 -36
package/README.md CHANGED
@@ -1,123 +1,158 @@
1
- # Codequiry - Node.js SDK
2
-
3
- Official Node.js SDK for [Codequiry's](https://codequiry.com) Code Plagiarism & Similarity Detection API.
4
-
5
- Check source code files against billions of web sources, public repositories, and peer submissions. Supports 65+ programming languages.
6
-
7
- ## Installation
8
-
9
- ```bash
10
- npm install codequiry
11
- ```
12
-
13
- ## Quick Start
14
-
15
- ```javascript
16
- const Codequiry = require('codequiry');
17
-
18
- const cq = new Codequiry('YOUR_API_KEY');
19
-
20
- // Create a check, upload files, start, and get results
21
- async function run() {
22
- // Create a check (language 14 = Python)
23
- const check = await cq.createCheck('Assignment 1', 14);
24
- console.log('Created:', check);
25
-
26
- // Upload a zip file
27
- await cq.uploadFile(check.check.id, './submissions.zip');
28
-
29
- // Start the check
30
- await cq.startCheck(check.check.id, { dbcheck: true });
31
-
32
- // Poll until complete
33
- const status = await cq.pollUntilComplete(check.check.id, {
34
- onProgress: (s) => console.log('Progress:', s),
35
- });
36
-
37
- // Get results
38
- const overview = await cq.getOverview(check.check.id);
39
- console.log('Results:', overview);
40
- }
41
-
42
- run().catch(console.error);
43
- ```
44
-
45
- ## API Reference
46
-
47
- ### Constructor
48
-
49
- ```javascript
50
- const cq = new Codequiry('YOUR_API_KEY');
51
- ```
52
-
53
- ### Account
54
-
55
- | Method | Description |
56
- |--------|-------------|
57
- | `cq.account()` | Get account info and usage quota |
58
-
59
- ### Checks
60
-
61
- | Method | Description |
62
- |--------|-------------|
63
- | `cq.checks()` | List all checks |
64
- | `cq.createCheck(name, languageId, testType?)` | Create a new check |
65
- | `cq.getCheck(checkId)` | Get check info and status |
66
- | `cq.deleteCheck(checkId)` | Delete a check |
67
-
68
- ### Upload
69
-
70
- | Method | Description |
71
- |--------|-------------|
72
- | `cq.uploadFile(checkId, filePath)` | Upload a ZIP file |
73
- | `cq.uploadBatch(checkId, filePaths)` | Upload multiple ZIP files |
74
-
75
- ### Start & Status
76
-
77
- | Method | Description |
78
- |--------|-------------|
79
- | `cq.startCheck(checkId, options?)` | Start a check. Options: `{ dbcheck, webcheck, testType }` |
80
- | `cq.getStatus(checkId)` | Get current check status |
81
- | `cq.pollUntilComplete(checkId, options?)` | Poll until done. Options: `{ interval, timeout, onProgress }` |
82
-
83
- ### Results
84
-
85
- | Method | Description |
86
- |--------|-------------|
87
- | `cq.getOverview(checkId)` | Results overview with similarity scores |
88
- | `cq.getResults(checkId, submissionId)` | Detailed results for a submission |
89
- | `cq.getSummary(checkId)` | Summary statistics |
90
-
91
- ### Reference Data
92
-
93
- | Method | Description |
94
- |--------|-------------|
95
- | `cq.getLanguages()` | List supported programming languages |
96
- | `cq.getTestTypes()` | List available check engine types |
97
-
98
- ## Supported Languages
99
-
100
- Java, Python, C, C++, C#, Perl, PHP, SQL, VB, XML, Haskell, Pascal, Go, Matlab, Lisp, Ruby, Assembly, HTML, JavaScript/TypeScript, Swift, Kotlin, Dart, Elixir, Jupyter Notebooks, and many more.
101
-
102
- ## Migration from v1
103
-
104
- v2 uses modern async/await instead of callbacks:
105
-
106
- ```javascript
107
- // v1 (old)
108
- Codequiry.setAPIKey('key');
109
- Codequiry.checks(function(data, err) {
110
- console.log(data);
111
- });
112
-
113
- // v2 (new)
114
- const cq = new Codequiry('key');
115
- const checks = await cq.checks();
116
- console.log(checks);
117
- ```
118
-
119
- ## Links
120
-
121
- - [Codequiry](https://codequiry.com)
122
- - [API Documentation](https://codequiry.com/usage/docs)
123
- - [CLI Tool](https://www.npmjs.com/package/codequiry-cli)
1
+ # Codequiry Node.js SDK — Code Plagiarism Checker API
2
+
3
+ [![npm version](https://img.shields.io/npm/v/codequiry.svg)](https://www.npmjs.com/package/codequiry)
4
+ [![license](https://img.shields.io/npm/l/codequiry.svg)](https://github.com/cqchecker/codequiry-sdk/blob/main/LICENSE)
5
+ [![node](https://img.shields.io/node/v/codequiry.svg)](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} [testType] - Test type ID (get from getTestTypes())
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, testType) {
59
- const body = { name, language };
60
- if (testType) body.test_type = testType;
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.1",
4
- "description": "Node.js SDK for Codequiry's Code Plagiarism & Similarity Detection API",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "echo \"No tests yet\""
8
- },
9
- "repository": {
10
- "type": "git",
11
- "url": "git+https://github.com/cqchecker/codequiry-sdk.git"
12
- },
13
- "keywords": [
14
- "codequiry",
15
- "plagiarism",
16
- "code-similarity",
17
- "code-plagiarism",
18
- "similarity-detection",
19
- "moss",
20
- "api",
21
- "sdk"
22
- ],
23
- "author": "Codequiry",
24
- "license": "MIT",
25
- "bugs": {
26
- "url": "https://github.com/cqchecker/codequiry-sdk/issues"
27
- },
28
- "homepage": "https://codequiry.com",
29
- "dependencies": {
30
- "axios": "^1.6.0",
31
- "form-data": "^4.0.0"
32
- },
33
- "engines": {
34
- "node": ">=14.0.0"
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
+ }