canvaslms-cli 1.3.3 ā 1.4.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/commands/announcements.js +102 -80
- package/commands/assignments.js +100 -130
- package/commands/config.js +239 -242
- package/commands/grades.js +201 -205
- package/commands/list.js +68 -70
- package/commands/profile.js +35 -34
- package/commands/submit.js +98 -151
- package/lib/api-client.js +4 -8
- package/lib/config-validator.js +60 -63
- package/lib/config.js +103 -135
- package/lib/file-upload.js +73 -78
- package/lib/interactive.js +180 -132
- package/package.json +4 -2
- package/src/index.js +25 -47
package/commands/submit.js
CHANGED
|
@@ -2,98 +2,84 @@
|
|
|
2
2
|
* Submit command for interactive assignment submission
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { makeCanvasRequest } from '../lib/api-client.js';
|
|
8
|
+
import { createReadlineInterface, askQuestion, askConfirmation, selectFilesImproved, pad } from '../lib/interactive.js';
|
|
9
|
+
import { uploadSingleFileToCanvas, submitAssignmentWithFiles } from '../lib/file-upload.js';
|
|
10
|
+
import chalk from 'chalk';
|
|
10
11
|
|
|
11
|
-
async function submitAssignment(options) {
|
|
12
|
+
export async function submitAssignment(options) {
|
|
12
13
|
const rl = createReadlineInterface();
|
|
13
14
|
|
|
14
15
|
try {
|
|
15
16
|
let courseId = options.course;
|
|
16
17
|
let assignmentId = options.assignment;
|
|
17
|
-
let filePath = options.file;
|
|
18
18
|
let selectedCourse = null;
|
|
19
19
|
let selectedAssignment = null;
|
|
20
20
|
|
|
21
21
|
// Step 1: Select Course (if not provided)
|
|
22
|
-
|
|
23
|
-
console.log('
|
|
24
|
-
|
|
22
|
+
while (!courseId) {
|
|
23
|
+
console.log(chalk.cyan.bold('\n' + '-'.repeat(60)));
|
|
24
|
+
console.log(chalk.cyan.bold('Loading your courses, please wait...'));
|
|
25
25
|
const courses = await makeCanvasRequest('get', 'courses', [
|
|
26
26
|
'enrollment_state=active',
|
|
27
27
|
'include[]=favorites'
|
|
28
28
|
]);
|
|
29
|
-
|
|
30
29
|
if (!courses || courses.length === 0) {
|
|
31
|
-
console.log('No courses found.');
|
|
30
|
+
console.log(chalk.red('Error: No courses found.'));
|
|
32
31
|
rl.close();
|
|
33
32
|
return;
|
|
34
33
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
let selectableCourses = courses;
|
|
35
|
+
if (!options.all) {
|
|
36
|
+
selectableCourses = courses.filter(course => course.is_favorite);
|
|
37
|
+
if (selectableCourses.length === 0) {
|
|
38
|
+
console.log(chalk.red('Error: No starred courses found. Showing all enrolled courses...'));
|
|
39
|
+
selectableCourses = courses;
|
|
40
|
+
}
|
|
42
41
|
}
|
|
43
|
-
|
|
44
|
-
console.log('Select a course:');
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
console.log(`${index + 1}. ${starIcon}${course.name}`);
|
|
42
|
+
console.log(chalk.cyan('-'.repeat(60)));
|
|
43
|
+
console.log(chalk.cyan.bold('Select a course:'));
|
|
44
|
+
selectableCourses.forEach((course, index) => {
|
|
45
|
+
console.log(pad(chalk.white((index + 1) + '. '), 5) + chalk.white(course.name));
|
|
48
46
|
});
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
// Handle empty input
|
|
52
|
-
if (!courseChoice.trim()) {
|
|
53
|
-
console.log('No course selected. Exiting...');
|
|
47
|
+
const courseChoice = await askQuestion(rl, chalk.bold.cyan('\nEnter course number (or ".."/"back" to cancel): '));
|
|
48
|
+
if (courseChoice === '..' || courseChoice.toLowerCase() === 'back') {
|
|
54
49
|
rl.close();
|
|
55
50
|
return;
|
|
56
51
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (courseIndex < 0 || courseIndex >= starredCourses.length) {
|
|
61
|
-
console.log('Invalid course selection.');
|
|
52
|
+
if (!courseChoice.trim()) {
|
|
53
|
+
console.log(chalk.red('Error: No course selected. Exiting...'));
|
|
62
54
|
rl.close();
|
|
63
55
|
return;
|
|
64
56
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} else {
|
|
70
|
-
// Fetch course details if ID was provided
|
|
71
|
-
try {
|
|
72
|
-
selectedCourse = await makeCanvasRequest('get', `courses/${courseId}`);
|
|
73
|
-
} catch (error) {
|
|
74
|
-
console.log(`ā ļø Could not fetch course details for ID ${courseId}`);
|
|
75
|
-
selectedCourse = { id: courseId, name: `Course ${courseId}` };
|
|
57
|
+
const courseIndex = parseInt(courseChoice) - 1;
|
|
58
|
+
if (courseIndex < 0 || courseIndex >= selectableCourses.length) {
|
|
59
|
+
console.log(chalk.red('Error: Invalid course selection.'));
|
|
60
|
+
continue;
|
|
76
61
|
}
|
|
62
|
+
selectedCourse = selectableCourses[courseIndex];
|
|
63
|
+
courseId = selectedCourse.id;
|
|
64
|
+
console.log(chalk.green(`Success: Selected ${selectedCourse.name}\n`));
|
|
77
65
|
}
|
|
78
66
|
|
|
79
67
|
// Step 2: Select Assignment (if not provided)
|
|
80
|
-
|
|
81
|
-
console.log('
|
|
82
|
-
|
|
68
|
+
while (!assignmentId) {
|
|
69
|
+
console.log(chalk.cyan.bold('-'.repeat(60)));
|
|
70
|
+
console.log(chalk.cyan.bold('Loading assignments, please wait...'));
|
|
83
71
|
const assignments = await makeCanvasRequest('get', `courses/${courseId}/assignments`, [
|
|
84
72
|
'include[]=submission',
|
|
85
73
|
'order_by=due_at',
|
|
86
74
|
'per_page=100'
|
|
87
75
|
]);
|
|
88
|
-
|
|
89
76
|
if (!assignments || assignments.length === 0) {
|
|
90
|
-
console.log('No assignments found for this course.');
|
|
77
|
+
console.log(chalk.red('Error: No assignments found for this course.'));
|
|
91
78
|
rl.close();
|
|
92
79
|
return;
|
|
93
80
|
}
|
|
94
|
-
|
|
95
|
-
console.log(`Found ${assignments.length} assignment(s)
|
|
96
|
-
|
|
81
|
+
console.log(chalk.cyan('-'.repeat(60)));
|
|
82
|
+
console.log(chalk.cyan.bold(`Found ${assignments.length} assignment(s):`));
|
|
97
83
|
// Show summary of assignment statuses
|
|
98
84
|
const submittedCount = assignments.filter(a => a.submission && a.submission.submitted_at).length;
|
|
99
85
|
const pendingCount = assignments.length - submittedCount;
|
|
@@ -102,23 +88,15 @@ async function submitAssignment(options) {
|
|
|
102
88
|
a.submission_types.includes('online_upload') &&
|
|
103
89
|
a.workflow_state === 'published'
|
|
104
90
|
).length;
|
|
105
|
-
|
|
106
|
-
console.log(
|
|
107
|
-
|
|
91
|
+
console.log(chalk.yellow(`Summary: ${submittedCount} submitted, ${pendingCount} pending, ${uploadableCount} accept file uploads`));
|
|
92
|
+
console.log(chalk.cyan('-'.repeat(60)));
|
|
93
|
+
// Numbered menu
|
|
108
94
|
assignments.forEach((assignment, index) => {
|
|
109
95
|
const dueDate = assignment.due_at ? new Date(assignment.due_at).toLocaleDateString() : 'No due date';
|
|
110
|
-
const submitted = assignment.submission && assignment.submission.submitted_at ? '
|
|
111
|
-
|
|
112
|
-
// Check if assignment accepts file uploads
|
|
113
|
-
const canSubmitFiles = assignment.submission_types &&
|
|
114
|
-
assignment.submission_types.includes('online_upload') &&
|
|
115
|
-
assignment.workflow_state === 'published';
|
|
116
|
-
const submissionIcon = canSubmitFiles ? 'š¤' : 'š';
|
|
117
|
-
|
|
118
|
-
// Format grade like Canvas web interface
|
|
96
|
+
const submitted = assignment.submission && assignment.submission.submitted_at ? chalk.green('Submitted') : chalk.yellow('Not submitted');
|
|
97
|
+
const canSubmitFiles = assignment.submission_types && assignment.submission_types.includes('online_upload') && assignment.workflow_state === 'published';
|
|
119
98
|
let gradeDisplay = '';
|
|
120
99
|
const submission = assignment.submission;
|
|
121
|
-
|
|
122
100
|
if (submission && submission.score !== null && submission.score !== undefined) {
|
|
123
101
|
const score = submission.score % 1 === 0 ? Math.round(submission.score) : submission.score;
|
|
124
102
|
const total = assignment.points_possible || 0;
|
|
@@ -130,145 +108,118 @@ async function submitAssignment(options) {
|
|
|
130
108
|
} else if (assignment.points_possible) {
|
|
131
109
|
gradeDisplay = ` | Grade: ā/${assignment.points_possible}`;
|
|
132
110
|
}
|
|
133
|
-
|
|
134
|
-
console.log(` Due: ${dueDate} | Points: ${assignment.points_possible || 'N/A'}${gradeDisplay}`);
|
|
111
|
+
let line = pad(chalk.white((index + 1) + '. '), 5) + chalk.white(assignment.name) + chalk.gray(` (${dueDate})`) + ' ' + submitted + gradeDisplay;
|
|
135
112
|
if (!canSubmitFiles) {
|
|
136
|
-
|
|
113
|
+
line += chalk.red(' [No file uploads]');
|
|
137
114
|
}
|
|
115
|
+
console.log(line);
|
|
138
116
|
});
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
117
|
+
const assignmentChoice = await askQuestion(rl, chalk.bold.cyan('\nEnter assignment number (or ".."/"back" to re-select course): '));
|
|
118
|
+
if (assignmentChoice === '..' || assignmentChoice.toLowerCase() === 'back') {
|
|
119
|
+
courseId = null;
|
|
120
|
+
selectedCourse = null;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
142
123
|
if (!assignmentChoice.trim()) {
|
|
143
|
-
console.log('No assignment selected. Exiting...');
|
|
124
|
+
console.log(chalk.red('Error: No assignment selected. Exiting...'));
|
|
144
125
|
rl.close();
|
|
145
126
|
return;
|
|
146
127
|
}
|
|
147
|
-
|
|
148
128
|
const assignmentIndex = parseInt(assignmentChoice) - 1;
|
|
149
|
-
|
|
150
129
|
if (assignmentIndex < 0 || assignmentIndex >= assignments.length) {
|
|
151
|
-
console.log('Invalid assignment selection.');
|
|
152
|
-
|
|
153
|
-
return;
|
|
130
|
+
console.log(chalk.red('Error: Invalid assignment selection.'));
|
|
131
|
+
continue;
|
|
154
132
|
}
|
|
155
|
-
|
|
156
133
|
selectedAssignment = assignments[assignmentIndex];
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
if (!selectedAssignment.submission_types ||
|
|
160
|
-
!selectedAssignment.submission_types.includes('online_upload') ||
|
|
161
|
-
selectedAssignment.workflow_state !== 'published') {
|
|
162
|
-
console.log('ā This assignment does not accept file uploads or is not published.');
|
|
134
|
+
if (!selectedAssignment.submission_types || !selectedAssignment.submission_types.includes('online_upload') || selectedAssignment.workflow_state !== 'published') {
|
|
135
|
+
console.log(chalk.red('Error: This assignment does not accept file uploads or is not published.'));
|
|
163
136
|
rl.close();
|
|
164
137
|
return;
|
|
165
138
|
}
|
|
166
|
-
|
|
167
139
|
assignmentId = selectedAssignment.id;
|
|
168
|
-
console.log(
|
|
169
|
-
|
|
170
|
-
// Check if already submitted
|
|
140
|
+
console.log(chalk.green(`Success: Selected ${selectedAssignment.name}\n`));
|
|
171
141
|
if (selectedAssignment.submission && selectedAssignment.submission.submitted_at) {
|
|
172
|
-
const resubmit = await askConfirmation(rl, 'This assignment has already been submitted. Do you want to resubmit?', false);
|
|
142
|
+
const resubmit = await askConfirmation(rl, chalk.yellow('This assignment has already been submitted. Do you want to resubmit?'), false);
|
|
173
143
|
if (!resubmit) {
|
|
174
|
-
console.log('Submission cancelled.');
|
|
144
|
+
console.log(chalk.yellow('Submission cancelled.'));
|
|
175
145
|
rl.close();
|
|
176
146
|
return;
|
|
177
147
|
}
|
|
178
148
|
}
|
|
179
|
-
} else {
|
|
180
|
-
// Fetch assignment details if ID was provided
|
|
181
|
-
try {
|
|
182
|
-
selectedAssignment = await makeCanvasRequest('get', `courses/${courseId}/assignments/${assignmentId}`);
|
|
183
|
-
console.log(`ā
Using assignment: ${selectedAssignment.name}\n`);
|
|
184
|
-
} catch (error) {
|
|
185
|
-
console.log(`ā ļø Could not fetch assignment details for ID ${assignmentId}`);
|
|
186
|
-
selectedAssignment = { id: assignmentId, name: `Assignment ${assignmentId}` };
|
|
187
|
-
}
|
|
188
|
-
} // Step 3: Select Files (if not provided)
|
|
189
|
-
let filePaths = [];
|
|
190
|
-
if (filePath) {
|
|
191
|
-
filePaths = [filePath]; // Single file provided via option
|
|
192
|
-
} else {
|
|
193
|
-
console.log('\nš File Selection');
|
|
194
|
-
console.log(`š Course: ${selectedCourse.name}`);
|
|
195
|
-
console.log(`š Assignment: ${selectedAssignment.name}\n`);
|
|
196
|
-
|
|
197
|
-
filePaths = await selectFilesImproved(rl);
|
|
198
149
|
}
|
|
199
|
-
|
|
150
|
+
// Step 3: Always list files in current directory and prompt user to select
|
|
151
|
+
let filePaths = [];
|
|
152
|
+
console.log(chalk.cyan.bold('-'.repeat(60)));
|
|
153
|
+
console.log(chalk.cyan.bold('File Selection'));
|
|
154
|
+
console.log(chalk.cyan('-'.repeat(60)));
|
|
155
|
+
console.log(chalk.white('Course: ') + chalk.bold(selectedCourse.name));
|
|
156
|
+
console.log(chalk.white('Assignment: ') + chalk.bold(selectedAssignment.name) + '\n');
|
|
157
|
+
filePaths = await selectFilesImproved(rl);
|
|
200
158
|
// Validate all selected files exist
|
|
201
159
|
const validFiles = [];
|
|
202
160
|
for (const file of filePaths) {
|
|
203
161
|
if (fs.existsSync(file)) {
|
|
204
162
|
validFiles.push(file);
|
|
205
163
|
} else {
|
|
206
|
-
console.log(
|
|
164
|
+
console.log(chalk.red('Error: File not found: ' + file));
|
|
207
165
|
}
|
|
208
166
|
}
|
|
209
|
-
|
|
210
167
|
if (validFiles.length === 0) {
|
|
211
|
-
console.log('No valid files selected.');
|
|
168
|
+
console.log(chalk.red('Error: No valid files selected.'));
|
|
212
169
|
rl.close();
|
|
213
170
|
return;
|
|
214
171
|
}
|
|
215
|
-
|
|
216
172
|
filePaths = validFiles;
|
|
217
|
-
|
|
218
173
|
// Step 4: Confirm and Submit
|
|
219
|
-
console.log('
|
|
220
|
-
console.log(
|
|
221
|
-
console.log(
|
|
222
|
-
console.log(
|
|
174
|
+
console.log(chalk.cyan.bold('-'.repeat(60)));
|
|
175
|
+
console.log(chalk.cyan.bold('Submission Summary:'));
|
|
176
|
+
console.log(chalk.cyan('-'.repeat(60)));
|
|
177
|
+
console.log(chalk.white('Course: ') + chalk.bold(selectedCourse?.name || 'Unknown Course'));
|
|
178
|
+
console.log(chalk.white('Assignment: ') + chalk.bold(selectedAssignment?.name || 'Unknown Assignment'));
|
|
179
|
+
console.log(chalk.white(`Files (${filePaths.length}):`));
|
|
223
180
|
filePaths.forEach((file, index) => {
|
|
224
181
|
const stats = fs.statSync(file);
|
|
225
182
|
const size = (stats.size / 1024).toFixed(1) + ' KB';
|
|
226
|
-
console.log(
|
|
183
|
+
console.log(pad(chalk.white((index + 1) + '.'), 5) + pad(path.basename(file), 35) + chalk.gray(size));
|
|
227
184
|
});
|
|
228
|
-
|
|
229
|
-
const confirm = await askConfirmation(rl, '\nProceed with submission?', true);
|
|
185
|
+
const confirm = await askConfirmation(rl, chalk.bold.cyan('\nProceed with submission?'), true);
|
|
230
186
|
if (!confirm) {
|
|
231
|
-
console.log('Submission cancelled.');
|
|
187
|
+
console.log(chalk.yellow('Submission cancelled.'));
|
|
232
188
|
rl.close();
|
|
233
189
|
return;
|
|
234
190
|
}
|
|
235
|
-
|
|
236
|
-
console.log('\nš Uploading files...');
|
|
237
|
-
|
|
191
|
+
console.log(chalk.cyan.bold('\nUploading files, please wait...'));
|
|
238
192
|
// Upload all files
|
|
239
193
|
const uploadedFileIds = [];
|
|
240
194
|
for (let i = 0; i < filePaths.length; i++) {
|
|
241
195
|
const currentFile = filePaths[i];
|
|
242
|
-
|
|
243
|
-
|
|
196
|
+
process.stdout.write(chalk.yellow(`Uploading ${i + 1}/${filePaths.length}: ${path.basename(currentFile)} ... `));
|
|
244
197
|
try {
|
|
245
198
|
const fileId = await uploadSingleFileToCanvas(courseId, assignmentId, currentFile);
|
|
246
199
|
uploadedFileIds.push(fileId);
|
|
247
|
-
console.log(
|
|
248
|
-
|
|
249
|
-
|
|
200
|
+
console.log(chalk.green('Success: Uploaded.'));
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.error(chalk.red(`Error: Failed to upload ${currentFile}: ${error.message}`));
|
|
203
|
+
const continueUpload = await askConfirmation(rl, chalk.yellow('Continue with remaining files?'), true);
|
|
250
204
|
if (!continueUpload) {
|
|
251
205
|
break;
|
|
252
206
|
}
|
|
253
207
|
}
|
|
254
208
|
}
|
|
255
|
-
|
|
256
|
-
if (uploadedFileIds.length
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
209
|
+
// Submit assignment with uploaded files
|
|
210
|
+
if (uploadedFileIds.length > 0) {
|
|
211
|
+
try {
|
|
212
|
+
console.log(chalk.cyan.bold('Submitting assignment, please wait...'));
|
|
213
|
+
await submitAssignmentWithFiles(courseId, assignmentId, uploadedFileIds);
|
|
214
|
+
console.log(chalk.green('Success: Assignment submitted successfully!'));
|
|
215
|
+
} catch (error) {
|
|
216
|
+
console.error(chalk.red('Error: Failed to submit assignment: ' + error.message));
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
console.log(chalk.red('Error: No files were uploaded. Submission not completed.'));
|
|
260
220
|
}
|
|
261
|
-
|
|
262
|
-
// Submit the assignment with all uploaded files
|
|
263
|
-
console.log('\nš Submitting assignment...');
|
|
264
|
-
const submission = await submitAssignmentWithFiles(courseId, assignmentId, uploadedFileIds);
|
|
265
|
-
|
|
266
|
-
console.log(`ā
Assignment submitted successfully with ${uploadedFileIds.length} file(s)!`);
|
|
267
|
-
console.log(`Submission ID: ${submission.id}`);
|
|
268
|
-
console.log(`Submitted at: ${new Date(submission.submitted_at).toLocaleString()}`);
|
|
269
|
-
|
|
270
221
|
} catch (error) {
|
|
271
|
-
console.error('
|
|
222
|
+
console.error(chalk.red('Error: Submission failed: ' + error.message));
|
|
272
223
|
} finally {
|
|
273
224
|
rl.close();
|
|
274
225
|
}
|
|
@@ -394,7 +345,3 @@ async function selectSingleFileFromList(rl, files) {
|
|
|
394
345
|
return [manualFile];
|
|
395
346
|
}
|
|
396
347
|
}
|
|
397
|
-
|
|
398
|
-
module.exports = {
|
|
399
|
-
submitAssignment
|
|
400
|
-
};
|
package/lib/api-client.js
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Canvas API client
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
import axios from 'axios';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import { getInstanceConfig } from './config.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Make Canvas API request
|
|
11
11
|
*/
|
|
12
|
-
async function makeCanvasRequest(method, endpoint, queryParams = [], requestBody = null) {
|
|
12
|
+
export async function makeCanvasRequest(method, endpoint, queryParams = [], requestBody = null) {
|
|
13
13
|
const instanceConfig = getInstanceConfig();
|
|
14
14
|
|
|
15
15
|
// Construct the full URL
|
|
@@ -73,7 +73,3 @@ async function makeCanvasRequest(method, endpoint, queryParams = [], requestBody
|
|
|
73
73
|
process.exit(1);
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
|
-
|
|
77
|
-
module.exports = {
|
|
78
|
-
makeCanvasRequest
|
|
79
|
-
};
|
package/lib/config-validator.js
CHANGED
|
@@ -1,63 +1,60 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration validation and setup helpers
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Check if configuration is valid and prompt setup if needed
|
|
10
|
-
*/
|
|
11
|
-
async function ensureConfig() {
|
|
12
|
-
if (!configExists()) {
|
|
13
|
-
console.log('
|
|
14
|
-
console.log('\
|
|
15
|
-
|
|
16
|
-
const rl = createReadlineInterface();
|
|
17
|
-
const setup = await askQuestion(rl, 'Would you like to set up your configuration now? (Y/n): ');
|
|
18
|
-
rl.close();
|
|
19
|
-
|
|
20
|
-
if (setup.toLowerCase() === 'n' || setup.toLowerCase() === 'no') {
|
|
21
|
-
console.log('\
|
|
22
|
-
console.log('
|
|
23
|
-
process.exit(1);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Import and run setup (dynamic import to avoid circular dependency)
|
|
27
|
-
const { setupConfig } =
|
|
28
|
-
await setupConfig();
|
|
29
|
-
|
|
30
|
-
// Check if setup was successful
|
|
31
|
-
if (!configExists()) {
|
|
32
|
-
console.log('\
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log('\
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Validate existing config
|
|
41
|
-
const config = readConfig();
|
|
42
|
-
if (!config || !config.domain || !config.token) {
|
|
43
|
-
console.log('
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Wrapper function to ensure config exists before running a command
|
|
52
|
-
*/
|
|
53
|
-
function requireConfig(
|
|
54
|
-
return async function(...args) {
|
|
55
|
-
await ensureConfig();
|
|
56
|
-
return
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
ensureConfig,
|
|
62
|
-
requireConfig
|
|
63
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Configuration validation and setup helpers
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { configExists, readConfig } from './config.js';
|
|
6
|
+
import { createReadlineInterface, askQuestion } from './interactive.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Check if configuration is valid and prompt setup if needed
|
|
10
|
+
*/
|
|
11
|
+
async function ensureConfig() {
|
|
12
|
+
if (!configExists()) {
|
|
13
|
+
console.log('No Canvas configuration found!');
|
|
14
|
+
console.log('\nLet\'s set up your Canvas CLI configuration...\n');
|
|
15
|
+
|
|
16
|
+
const rl = createReadlineInterface();
|
|
17
|
+
const setup = await askQuestion(rl, 'Would you like to set up your configuration now? (Y/n): ');
|
|
18
|
+
rl.close();
|
|
19
|
+
|
|
20
|
+
if (setup.toLowerCase() === 'n' || setup.toLowerCase() === 'no') {
|
|
21
|
+
console.log('\nTo set up later, run: canvas config setup');
|
|
22
|
+
console.log('Or set environment variables: CANVAS_DOMAIN and CANVAS_API_TOKEN');
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Import and run setup (dynamic import to avoid circular dependency)
|
|
27
|
+
const { setupConfig } = await import('../commands/config.js');
|
|
28
|
+
await setupConfig();
|
|
29
|
+
|
|
30
|
+
// Check if setup was successful
|
|
31
|
+
if (!configExists()) {
|
|
32
|
+
console.log('\nConfiguration setup was not completed. Please run "canvas config setup" to try again.');
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
console.log('\nConfiguration complete! You can now use Canvas CLI commands.');
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Validate existing config
|
|
41
|
+
const config = readConfig();
|
|
42
|
+
if (!config || !config.domain || !config.token) {
|
|
43
|
+
console.log('Invalid configuration found. Please run "canvas config setup" to reconfigure.');
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Wrapper function to ensure config exists before running a command
|
|
52
|
+
*/
|
|
53
|
+
export function requireConfig(fn) {
|
|
54
|
+
return async function(...args) {
|
|
55
|
+
await ensureConfig();
|
|
56
|
+
return fn(...args);
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export { ensureConfig };
|